/*******************************************************************************
* Copyright (c) 2002, 2015 Innoopract Informationssysteme GmbH 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:
* Innoopract Informationssysteme GmbH - initial API and implementation
* EclipseSource - ongoing development
******************************************************************************/
package org.eclipse.swt.internal.widgets.treekit;
import static org.eclipse.rap.rwt.internal.lifecycle.WidgetLCAUtil.getStyles;
import static org.eclipse.rap.rwt.internal.lifecycle.WidgetLCAUtil.hasChanged;
import static org.eclipse.rap.rwt.internal.lifecycle.WidgetLCAUtil.preserveProperty;
import static org.eclipse.rap.rwt.internal.lifecycle.WidgetLCAUtil.renderListenDefaultSelection;
import static org.eclipse.rap.rwt.internal.lifecycle.WidgetLCAUtil.renderListenSelection;
import static org.eclipse.rap.rwt.internal.lifecycle.WidgetLCAUtil.renderProperty;
import static org.eclipse.rap.rwt.internal.lifecycle.WidgetUtil.getAdapter;
import static org.eclipse.rap.rwt.internal.lifecycle.WidgetUtil.getId;
import static org.eclipse.rap.rwt.internal.protocol.JsonUtil.createJsonArray;
import static org.eclipse.rap.rwt.internal.protocol.RemoteObjectFactory.createRemoteObject;
import static org.eclipse.rap.rwt.internal.protocol.RemoteObjectFactory.getRemoteObject;
import static org.eclipse.swt.internal.widgets.MarkupUtil.isMarkupEnabledFor;
import java.io.IOException;
import org.eclipse.rap.json.JsonArray;
import org.eclipse.rap.rwt.internal.lifecycle.WidgetLCA;
import org.eclipse.rap.rwt.internal.lifecycle.ControlLCAUtil;
import org.eclipse.rap.rwt.internal.lifecycle.WidgetLCAUtil;
import org.eclipse.rap.rwt.internal.template.TemplateLCAUtil;
import org.eclipse.rap.rwt.remote.RemoteObject;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.internal.widgets.CellToolTipUtil;
import org.eclipse.swt.internal.widgets.ICellToolTipAdapter;
import org.eclipse.swt.internal.widgets.IItemHolderAdapter;
import org.eclipse.swt.internal.widgets.ITreeAdapter;
import org.eclipse.swt.internal.widgets.WidgetRemoteAdapter;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
public final class TreeLCA extends WidgetLCA<Tree> {
public static final TreeLCA INSTANCE = new TreeLCA();
private static final String TYPE = "rwt.widgets.Grid";
private static final String[] ALLOWED_STYLES = {
"SINGLE",
"MULTI",
"CHECK",
"FULL_SELECTION",
"VIRTUAL",
"NO_SCROLL",
"NO_RADIO_GROUP",
"BORDER"
};
private static final String PROP_ITEM_COUNT = "itemCount";
private static final String PROP_ITEM_HEIGHT = "itemHeight";
private static final String PROP_ITEM_METRICS = "itemMetrics";
private static final String PROP_COLUMN_COUNT = "columnCount";
private static final String PROP_COLUMN_ORDER = "columnOrder";
private static final String PROP_FIXED_COLUMNS = "fixedColumns";
private static final String PROP_TREE_COLUMN = "treeColumn";
private static final String PROP_HEADER_HEIGHT = "headerHeight";
private static final String PROP_HEADER_VISIBLE = "headerVisible";
private static final String PROP_LINES_VISIBLE = "linesVisible";
private static final String PROP_TOP_ITEM_INDEX = "topItemIndex";
private static final String PROP_FOCUS_ITEM = "focusItem";
private static final String PROP_SCROLL_LEFT = "scrollLeft";
private static final String PROP_SELECTION = "selection";
private static final String PROP_SORT_DIRECTION = "sortDirection";
private static final String PROP_SORT_COLUMN = "sortColumn";
private static final String PROP_SETDATA_LISTENER = "SetData";
private static final String PROP_EXPAND_LISTENER = "Expand";
private static final String PROP_COLLAPSE_LISTENER = "Collapse";
private static final String PROP_ENABLE_CELL_TOOLTIP = "enableCellToolTip";
private static final String PROP_CELL_TOOLTIP_TEXT = "cellToolTipText";
private static final String PROP_MARKUP_ENABLED = "markupEnabled";
private static final int ZERO = 0 ;
private static final String[] DEFAULT_SELECTION = new String[ 0 ];
private static final String[] DEFAULT_COLUMN_ORDER = new String[ 0 ];
private static final String DEFAULT_SORT_DIRECTION = "none";
@Override
public void preserveValues( Tree tree ) {
preserveProperty( tree, PROP_ITEM_COUNT, tree.getItemCount() );
preserveProperty( tree, PROP_ITEM_HEIGHT, tree.getItemHeight() );
preserveProperty( tree, PROP_ITEM_METRICS, getItemMetrics( tree ) );
preserveProperty( tree, PROP_COLUMN_COUNT, tree.getColumnCount() );
preserveProperty( tree, PROP_COLUMN_ORDER, getColumnOrder( tree ) );
preserveProperty( tree, PROP_FIXED_COLUMNS, getFixedColumns( tree ) );
preserveProperty( tree, PROP_TREE_COLUMN, getTreeColumn( tree ) );
preserveProperty( tree, PROP_HEADER_HEIGHT, tree.getHeaderHeight() );
preserveProperty( tree, PROP_HEADER_VISIBLE, tree.getHeaderVisible() );
preserveProperty( tree, PROP_LINES_VISIBLE, tree.getLinesVisible() );
preserveProperty( tree, PROP_TOP_ITEM_INDEX, getTopItemIndex( tree ) );
preserveProperty( tree, PROP_FOCUS_ITEM, getFocusItem( tree ) );
preserveProperty( tree, PROP_SCROLL_LEFT, getScrollLeft( tree ) );
preserveProperty( tree, PROP_SELECTION, getSelection( tree ) );
preserveProperty( tree, PROP_SORT_DIRECTION, getSortDirection( tree ) );
preserveProperty( tree, PROP_SORT_COLUMN, tree.getSortColumn() );
preserveProperty( tree, PROP_ENABLE_CELL_TOOLTIP, CellToolTipUtil.isEnabledFor( tree ) );
preserveProperty( tree, PROP_CELL_TOOLTIP_TEXT, null );
}
@Override
public void renderInitialization( Tree tree ) throws IOException {
RemoteObject remoteObject = createRemoteObject( tree, TYPE );
remoteObject.setHandler( new TreeOperationHandler( tree ) );
remoteObject.set( "parent", getId( tree.getParent() ) );
remoteObject.set( "style", createJsonArray( getStyles( tree, ALLOWED_STYLES ) ) );
remoteObject.set( "appearance", "tree" );
ITreeAdapter adapter = getTreeAdapter( tree );
if( ( tree.getStyle() & SWT.CHECK ) != 0 ) {
JsonArray metrics = new JsonArray()
.add( adapter.getCheckLeft() )
.add( adapter.getCheckWidth() );
remoteObject.set( "checkBoxMetrics", metrics );
}
if( getFixedColumns( tree ) >= 0 ) {
remoteObject.set( "splitContainer", true );
}
if( ( tree.getStyle() & SWT.FULL_SELECTION ) == 0 ) {
Rectangle textMargin = getTreeAdapter( tree ).getTextMargin();
JsonArray padding = new JsonArray()
.add( textMargin.x )
.add( textMargin.width - textMargin.x );
remoteObject.set( "selectionPadding", padding );
}
remoteObject.set( "indentionWidth", adapter.getIndentionWidth() );
remoteObject.set( PROP_MARKUP_ENABLED, isMarkupEnabledFor( tree ) );
TemplateLCAUtil.renderRowTemplate( tree );
remoteObject.listen( PROP_SETDATA_LISTENER, isVirtual( tree ) );
// Always render listen for Expand and Collapse, currently required for scrollbar
// visibility update and setData events.
remoteObject.listen( PROP_EXPAND_LISTENER, true );
remoteObject.listen( PROP_COLLAPSE_LISTENER, true );
}
@Override
public void renderChanges( final Tree tree ) throws IOException {
ControlLCAUtil.renderChanges( tree );
WidgetLCAUtil.renderCustomVariant( tree );
renderProperty( tree, PROP_ITEM_COUNT, tree.getItemCount(), ZERO );
renderProperty( tree, PROP_ITEM_HEIGHT, tree.getItemHeight(), ZERO );
renderItemMetrics( tree );
renderProperty( tree, PROP_COLUMN_COUNT, tree.getColumnCount(), ZERO );
renderProperty( tree, PROP_COLUMN_ORDER, getColumnOrder( tree ), DEFAULT_COLUMN_ORDER );
renderProperty( tree, PROP_FIXED_COLUMNS, getFixedColumns( tree ), -1 );
renderProperty( tree, PROP_TREE_COLUMN, getTreeColumn( tree ), ZERO );
renderProperty( tree, PROP_HEADER_HEIGHT, tree.getHeaderHeight(), ZERO );
renderProperty( tree, PROP_HEADER_VISIBLE, tree.getHeaderVisible(), false );
renderProperty( tree, PROP_LINES_VISIBLE, tree.getLinesVisible(), false );
renderProperty( tree, PROP_SORT_DIRECTION, getSortDirection( tree ), DEFAULT_SORT_DIRECTION );
renderAfterItems( tree, new Runnable() {
@Override
public void run() {
renderProperty( tree, PROP_TOP_ITEM_INDEX, getTopItemIndex( tree ), ZERO );
renderProperty( tree, PROP_SCROLL_LEFT, getScrollLeft( tree ), ZERO );
if( tree.getSelectionCount() > 0 ) {
renderProperty( tree, PROP_FOCUS_ITEM, getFocusItem( tree ), null );
}
renderProperty( tree, PROP_SELECTION, getSelection( tree ), DEFAULT_SELECTION );
renderProperty( tree, PROP_SORT_COLUMN, tree.getSortColumn(), null );
}
} );
renderListenSelection( tree );
renderListenDefaultSelection( tree );
renderProperty( tree, PROP_ENABLE_CELL_TOOLTIP, CellToolTipUtil.isEnabledFor( tree ), false );
renderProperty( tree, PROP_CELL_TOOLTIP_TEXT, getAndResetCellToolTipText( tree ), null );
}
@Override
public void doRedrawFake( Control control ) {
Tree tree = ( Tree )control;
getTreeAdapter( tree ).checkData();
}
private static String getAndResetCellToolTipText( Tree tree ) {
ICellToolTipAdapter adapter = CellToolTipUtil.getAdapter( tree );
String toolTipText = adapter.getCellToolTipText();
adapter.setCellToolTipText( null );
return toolTipText;
}
private static boolean isVirtual( Tree tree ) {
return ( tree.getStyle() & SWT.VIRTUAL ) != 0;
}
private static String[] getSelection( Tree tree ) {
TreeItem[] selection = tree.getSelection();
String[] result = new String[ selection.length ];
for( int i = 0; i < result.length; i++ ) {
result[ i ] = getId( selection[ i ] );
}
return result;
}
private static String[] getColumnOrder( Tree tree ) {
int[] order = tree.getColumnOrder();
String[] result = new String[ order.length ];
for( int i = 0; i < result.length; i++ ) {
result[ i ] = getId( tree.getColumn( order[ i ] ) );
}
return result;
}
private static int getFixedColumns( Tree tree ) {
return getTreeAdapter( tree ).getFixedColumns();
}
private static int getScrollLeft( Tree tree ) {
return getTreeAdapter( tree ).getScrollLeft();
}
private static int getTopItemIndex( Tree tree ) {
return getTreeAdapter( tree ).getTopItemIndex();
}
private static TreeItem getFocusItem( Tree tree ) {
TreeItem result = null;
TreeItem[] selection = tree.getSelection();
if( selection.length > 0 ) {
result = selection[ 0 ];
}
return result;
}
private static int getTreeColumn( Tree tree ) {
int[] values = tree.getColumnOrder();
return values.length > 0 ? values[ 0 ] : 0;
}
private static String getSortDirection( Tree tree ) {
String result = "none";
if( tree.getSortDirection() == SWT.UP ) {
result = "up";
} else if( tree.getSortDirection() == SWT.DOWN ) {
result = "down";
}
return result;
}
private static ITreeAdapter getTreeAdapter( Tree tree ) {
return tree.getAdapter( ITreeAdapter.class );
}
private static void renderAfterItems( Tree tree, Runnable runnable ) {
Item[] items = tree.getAdapter( IItemHolderAdapter.class ).getItems();
if( items.length > 0 ) {
Item lastItem = items[ items.length - 1 ];
WidgetRemoteAdapter adapter = ( WidgetRemoteAdapter )getAdapter( lastItem );
adapter.addRenderRunnable( runnable );
} else {
runnable.run();
}
}
private static void renderItemMetrics( Tree tree ) {
ItemMetrics[] itemMetrics = getItemMetrics( tree );
if( hasChanged( tree, PROP_ITEM_METRICS, itemMetrics ) ) {
JsonArray metrics = new JsonArray();
for( int i = 0; i < itemMetrics.length; i++ ) {
metrics.add( new JsonArray().add( i )
.add( itemMetrics[ i ].left )
.add( itemMetrics[ i ].width )
.add( itemMetrics[ i ].imageLeft )
.add( itemMetrics[ i ].imageWidth )
.add( itemMetrics[ i ].textLeft )
.add( itemMetrics[ i ].textWidth ) );
}
getRemoteObject( tree ).set( PROP_ITEM_METRICS, metrics );
}
}
static ItemMetrics[] getItemMetrics( Tree tree ) {
int columnCount = Math.max( 1, tree.getColumnCount() );
ItemMetrics[] result = new ItemMetrics[ columnCount ];
for( int i = 0; i < columnCount; i++ ) {
result[ i ] = new ItemMetrics();
}
ITreeAdapter adapter = getTreeAdapter( tree );
for( int i = 0; i < columnCount; i++ ) {
result[ i ].left = adapter.getCellLeft( i );
result[ i ].width = adapter.getCellWidth( i );
result[ i ].imageLeft = result[ i ].left + adapter.getImageOffset( i );
result[ i ].imageWidth = adapter.getItemImageSize( i ).x;
result[ i ].textLeft = result[ i ].left + adapter.getTextOffset( i );
result[ i ].textWidth = adapter.getTextMaxWidth( i );
}
return result;
}
// TODO: merge with Table:
static final class ItemMetrics {
int left;
int width;
int imageLeft;
int imageWidth;
int textLeft;
int textWidth;
@Override
public boolean equals( Object obj ) {
boolean result;
if( obj == this ) {
result = true;
} else if( obj instanceof ItemMetrics ) {
ItemMetrics other = ( ItemMetrics )obj;
result = other.left == left
&& other.width == width
&& other.imageLeft == imageLeft
&& other.imageWidth == imageWidth
&& other.textLeft == textLeft
&& other.textWidth == textWidth;
} else {
result = false;
}
return result;
}
@Override
public int hashCode() {
String msg = "ItemMetrics#hashCode() not implemented";
throw new UnsupportedOperationException( msg );
}
}
private TreeLCA() {
// prevent instantiation
}
}