blob: 9b565e526a2c91e4cd550ce620866a6b5b1acffa [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 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.nebula.widgets.grid.internal.gridkit;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_PARAM_DETAIL;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_PARAM_INDEX;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_PARAM_ITEM;
import static org.eclipse.rap.rwt.internal.protocol.JsonUtil.createJsonArray;
import static org.eclipse.rap.rwt.internal.protocol.ProtocolUtil.readCallPropertyValueAsString;
import static org.eclipse.rap.rwt.internal.protocol.ProtocolUtil.readPropertyValueAsStringArray;
import static org.eclipse.rap.rwt.internal.protocol.ProtocolUtil.wasCallReceived;
import static org.eclipse.rap.rwt.internal.protocol.RemoteObjectFactory.createRemoteObject;
import static org.eclipse.rap.rwt.internal.protocol.RemoteObjectFactory.getRemoteObject;
import static org.eclipse.rap.rwt.lifecycle.WidgetLCAUtil.getStyles;
import static org.eclipse.rap.rwt.lifecycle.WidgetLCAUtil.preserveListener;
import static org.eclipse.rap.rwt.lifecycle.WidgetLCAUtil.preserveProperty;
import static org.eclipse.rap.rwt.lifecycle.WidgetLCAUtil.readEventPropertyValue;
import static org.eclipse.rap.rwt.lifecycle.WidgetLCAUtil.readPropertyValue;
import static org.eclipse.rap.rwt.lifecycle.WidgetLCAUtil.renderListener;
import static org.eclipse.rap.rwt.lifecycle.WidgetLCAUtil.renderProperty;
import static org.eclipse.rap.rwt.lifecycle.WidgetLCAUtil.wasEventSent;
import static org.eclipse.rap.rwt.lifecycle.WidgetUtil.getId;
import static org.eclipse.swt.internal.events.EventLCAUtil.isListening;
import java.io.IOException;
import org.eclipse.nebula.widgets.grid.Grid;
import org.eclipse.nebula.widgets.grid.GridColumn;
import org.eclipse.nebula.widgets.grid.GridItem;
import org.eclipse.nebula.widgets.grid.internal.IGridAdapter;
import org.eclipse.rap.json.JsonArray;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.internal.protocol.ClientMessageConst;
import org.eclipse.rap.rwt.internal.util.NumberFormatUtil;
import org.eclipse.rap.rwt.lifecycle.AbstractWidgetLCA;
import org.eclipse.rap.rwt.lifecycle.ControlLCAUtil;
import org.eclipse.rap.rwt.lifecycle.WidgetLCAUtil;
import org.eclipse.rap.rwt.lifecycle.WidgetUtil;
import org.eclipse.rap.rwt.remote.RemoteObject;
import org.eclipse.swt.SWT;
import org.eclipse.swt.internal.widgets.CellToolTipUtil;
import org.eclipse.swt.internal.widgets.ICellToolTipAdapter;
import org.eclipse.swt.internal.widgets.ICellToolTipProvider;
import org.eclipse.swt.internal.widgets.ScrollBarLCAUtil;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Widget;
@SuppressWarnings("restriction")
public class GridLCA extends AbstractWidgetLCA {
private static final String TYPE = "rwt.widgets.Grid";
private static final String[] ALLOWED_STYLES = new String[] {
"SINGLE",
"MULTI",
"FULL_SELECTION",
"VIRTUAL",
"NO_FOCUS",
"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_TREE_COLUMN = "treeColumn";
private static final String PROP_HEADER_HEIGHT = "headerHeight";
private static final String PROP_HEADER_VISIBLE = "headerVisible";
private static final String PROP_FOOTER_HEIGHT = "footerHeight";
private static final String PROP_FOOTER_VISIBLE = "footerVisible";
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";
// TODO: [if] Sync sortDirection and sortColumn in GridColumnLCA when multiple sort columns are
// possible on the client
private static final String PROP_SORT_DIRECTION = "sortDirection";
private static final String PROP_SORT_COLUMN = "sortColumn";
private static final String PROP_SELECTION_LISTENER = "Selection";
private static final String PROP_DEFAULT_SELECTION_LISTENER = "DefaultSelection";
private static final String PROP_SETDATA_LISTENER = "SetData";
private static final String PROP_EXPAND_LISTENER = "Expand";
private static final String PROP_COLLAPSE_LISTENER = "Collapse";
// TODO: [if] Sync toolTipText in GridItemLCA when it's possible on the client
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_SORT_DIRECTION = "none";
@Override
public void renderInitialization( Widget widget ) throws IOException {
Grid grid = ( Grid )widget;
RemoteObject remoteObject = createRemoteObject( grid, TYPE );
remoteObject.set( "parent", getId( grid.getParent() ) );
remoteObject.set( "style", createJsonArray( getStyles( grid, ALLOWED_STYLES ) ) );
remoteObject.set( "appearance", "tree" );
IGridAdapter adapter = getGridAdapter( grid );
remoteObject.set( "indentionWidth", adapter.getIndentationWidth() );
remoteObject.set( PROP_MARKUP_ENABLED, isMarkupEnabled( grid ) );
ScrollBarLCAUtil.renderInitialization( grid );
}
public void readData( Widget widget ) {
Grid grid = ( Grid )widget;
readSelection( grid );
readScrollLeft( grid );
readTopItemIndex( grid );
readFocusItem( grid );
readCellToolTipTextRequested( grid );
processSelectionEvent( grid, ClientMessageConst.EVENT_SELECTION );
processSelectionEvent( grid, ClientMessageConst.EVENT_DEFAULT_SELECTION );
processTreeEvent( grid, SWT.Expand, ClientMessageConst.EVENT_EXPAND );
processTreeEvent( grid, SWT.Collapse, ClientMessageConst.EVENT_COLLAPSE );
ControlLCAUtil.processEvents( grid );
ControlLCAUtil.processKeyEvents( grid );
ControlLCAUtil.processMenuDetect( grid );
WidgetLCAUtil.processHelp( grid );
ScrollBarLCAUtil.processSelectionEvent( grid );
}
@Override
public void preserveValues( Widget widget ) {
Grid grid = ( Grid )widget;
ControlLCAUtil.preserveValues( ( Control )widget );
WidgetLCAUtil.preserveCustomVariant( grid );
preserveProperty( grid, PROP_ITEM_COUNT, grid.getRootItemCount() );
preserveProperty( grid, PROP_ITEM_HEIGHT, grid.getItemHeight() );
preserveProperty( grid, PROP_ITEM_METRICS, getItemMetrics( grid ) );
preserveProperty( grid, PROP_COLUMN_COUNT, grid.getColumnCount() );
preserveProperty( grid, PROP_TREE_COLUMN, getTreeColumn( grid ) );
preserveProperty( grid, PROP_HEADER_HEIGHT, grid.getHeaderHeight() );
preserveProperty( grid, PROP_HEADER_VISIBLE, grid.getHeaderVisible() );
preserveProperty( grid, PROP_FOOTER_HEIGHT, grid.getFooterHeight() );
preserveProperty( grid, PROP_FOOTER_VISIBLE, grid.getFooterVisible() );
preserveProperty( grid, PROP_LINES_VISIBLE, grid.getLinesVisible() );
preserveProperty( grid, PROP_TOP_ITEM_INDEX, getTopItemIndex( grid ) );
preserveProperty( grid, PROP_FOCUS_ITEM, grid.getFocusItem() );
preserveProperty( grid, PROP_SCROLL_LEFT, getScrollLeft( grid ) );
preserveProperty( grid, PROP_SELECTION, getSelection( grid ) );
preserveProperty( grid, PROP_SORT_DIRECTION, getSortDirection( grid ) );
preserveProperty( grid, PROP_SORT_COLUMN, getSortColumn( grid ) );
preserveListener( grid, PROP_SELECTION_LISTENER, isListening( grid, SWT.Selection ) );
preserveListener( grid,
PROP_DEFAULT_SELECTION_LISTENER,
isListening( grid, SWT.DefaultSelection ) );
preserveListener( grid, PROP_SETDATA_LISTENER, listensToSetData( grid ) );
preserveListener( grid, PROP_EXPAND_LISTENER, hasExpandListener( grid ) );
preserveListener( grid, PROP_COLLAPSE_LISTENER, hasCollapseListener( grid ) );
preserveProperty( grid, PROP_ENABLE_CELL_TOOLTIP, CellToolTipUtil.isEnabledFor( grid ) );
preserveProperty( grid, PROP_CELL_TOOLTIP_TEXT, null );
ScrollBarLCAUtil.preserveValues( grid );
}
@Override
public void renderChanges( Widget widget ) throws IOException {
Grid grid = ( Grid )widget;
ControlLCAUtil.renderChanges( grid );
WidgetLCAUtil.renderCustomVariant( grid );
renderProperty( grid, PROP_ITEM_COUNT, grid.getRootItemCount(), ZERO );
renderProperty( grid, PROP_ITEM_HEIGHT, grid.getItemHeight(), ZERO );
renderItemMetrics( grid );
renderProperty( grid, PROP_COLUMN_COUNT, grid.getColumnCount(), ZERO );
renderProperty( grid, PROP_TREE_COLUMN, getTreeColumn( grid ), ZERO );
renderProperty( grid, PROP_HEADER_HEIGHT, grid.getHeaderHeight(), ZERO );
renderProperty( grid, PROP_HEADER_VISIBLE, grid.getHeaderVisible(), false );
renderProperty( grid, PROP_FOOTER_HEIGHT, grid.getFooterHeight(), ZERO );
renderProperty( grid, PROP_FOOTER_VISIBLE, grid.getFooterVisible(), false );
renderProperty( grid, PROP_LINES_VISIBLE, grid.getLinesVisible(), false );
renderProperty( grid, PROP_TOP_ITEM_INDEX, getTopItemIndex( grid ), ZERO );
renderProperty( grid, PROP_FOCUS_ITEM, grid.getFocusItem(), null );
renderProperty( grid, PROP_SCROLL_LEFT, getScrollLeft( grid ), ZERO );
renderProperty( grid, PROP_SELECTION, getSelection( grid ), DEFAULT_SELECTION );
renderProperty( grid, PROP_SORT_DIRECTION, getSortDirection( grid ), DEFAULT_SORT_DIRECTION );
renderProperty( grid, PROP_SORT_COLUMN, getSortColumn( grid ), null );
renderListener( grid, PROP_SELECTION_LISTENER, isListening( grid, SWT.Selection ), false );
renderListener( grid,
PROP_DEFAULT_SELECTION_LISTENER,
isListening( grid, SWT.DefaultSelection ),
false );
renderListener( grid, PROP_SETDATA_LISTENER, listensToSetData( grid ), false );
renderListener( grid, PROP_EXPAND_LISTENER, hasExpandListener( grid ), false );
renderListener( grid, PROP_COLLAPSE_LISTENER, hasCollapseListener( grid ), false );
renderProperty( grid, PROP_ENABLE_CELL_TOOLTIP, CellToolTipUtil.isEnabledFor( grid ), false );
renderProperty( grid, PROP_CELL_TOOLTIP_TEXT, getCellToolTipText( grid ), null );
ScrollBarLCAUtil.renderChanges( grid );
}
@Override
public void doRedrawFake( Control control ) {
getGridAdapter( ( Grid )control ).doRedraw();
}
////////////////////////////////////////////
// Helping methods to read client-side state
private static void readSelection( Grid grid ) {
String[] values = readPropertyValueAsStringArray( getId( grid ), "selection" );
if( values != null ) {
GridItem[] selectedItems = new GridItem[ values.length ];
boolean validItemFound = false;
for( int i = 0; i < values.length; i++ ) {
selectedItems[ i ] = getItem( grid, values[ i ] );
if( selectedItems[ i ] != null ) {
validItemFound = true;
}
}
if( !validItemFound ) {
selectedItems = new GridItem[ 0 ];
}
grid.setSelection( selectedItems );
}
}
private static void readScrollLeft( Grid grid ) {
String left = readPropertyValue( grid, "scrollLeft" );
if( left != null ) {
int leftOffset = NumberFormatUtil.parseInt( left );
processScrollBarSelection( grid.getHorizontalBar(), leftOffset );
}
}
private static void readTopItemIndex( Grid grid ) {
String topItemIndex = readPropertyValue( grid, "topItemIndex" );
if( topItemIndex != null ) {
int topOffset = NumberFormatUtil.parseInt( topItemIndex );
getGridAdapter( grid ).invalidateTopIndex();
processScrollBarSelection( grid.getVerticalBar(), topOffset );
}
}
private static void readFocusItem( Grid grid ) {
String value = readPropertyValue( grid, "focusItem" );
if( value != null ) {
GridItem item = getItem( grid, value );
if( item != null ) {
grid.setFocusItem( item );
}
}
}
////////////////
// Cell tooltips
private static void readCellToolTipTextRequested( Grid grid ) {
ICellToolTipAdapter adapter = CellToolTipUtil.getAdapter( grid );
adapter.setCellToolTipText( null );
ICellToolTipProvider provider = adapter.getCellToolTipProvider();
String methodName = "renderToolTipText";
if( provider != null && wasCallReceived( getId( grid ), methodName ) ) {
String itemId = readCallPropertyValueAsString( getId( grid ), methodName, "item" );
String column = readCallPropertyValueAsString( getId( grid ), methodName, "column" );
int columnIndex = NumberFormatUtil.parseInt( column );
GridItem item = getItem( grid, itemId );
if( item != null && ( columnIndex == 0 || columnIndex < grid.getColumnCount() ) ) {
provider.getToolTipText( item, columnIndex );
}
}
}
private static String getCellToolTipText( Grid grid ) {
return CellToolTipUtil.getAdapter( grid ).getCellToolTipText();
}
//////////////////
// Helping methods
private boolean listensToSetData( Grid grid ) {
return ( grid.getStyle() & SWT.VIRTUAL ) != 0;
}
private static boolean isMarkupEnabled( Grid grid ) {
return Boolean.TRUE.equals( grid.getData( RWT.MARKUP_ENABLED ) );
}
private static int getTreeColumn( Grid grid ) {
int[] order = grid.getColumnOrder();
return order.length > 0 ? order[ 0 ] : 0;
}
private static int getTopItemIndex( Grid grid ) {
int result = 0;
ScrollBar verticalBar = grid.getVerticalBar();
if( verticalBar != null ) {
result = verticalBar.getSelection();
}
return result;
}
private static int getScrollLeft( Grid grid ) {
int result = 0;
ScrollBar horizontalBar = grid.getHorizontalBar();
if( horizontalBar != null ) {
result = horizontalBar.getSelection();
}
return result;
}
private static String[] getSelection( Grid grid ) {
GridItem[] selection = grid.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 getSortDirection( Grid grid ) {
String result = "none";
for( int i = 0; i < grid.getColumnCount() && result.equals( "none" ); i++ ) {
int sort = grid.getColumn( i ).getSort();
if( sort == SWT.UP ) {
result = "up";
} else if( sort == SWT.DOWN ) {
result = "down";
}
}
return result;
}
private static GridColumn getSortColumn( Grid grid ) {
GridColumn result = null;
for( int i = 0; i < grid.getColumnCount() && result == null; i++ ) {
GridColumn column = grid.getColumn( i );
if( column.getSort() != SWT.NONE ) {
result = column;
}
}
return result;
}
private static GridItem getItem( Grid grid, String itemId ) {
return ( GridItem )WidgetUtil.find( grid, itemId );
}
private static void processScrollBarSelection( ScrollBar scrollBar, int selection ) {
if( scrollBar != null ) {
scrollBar.setSelection( selection );
}
}
private static void processSelectionEvent( Grid grid, String eventName ) {
if( wasEventSent( grid, eventName ) ) {
GridItem item = getItem( grid, readEventPropertyValue( grid, eventName, EVENT_PARAM_ITEM ) );
if( item != null ) {
if( eventName.equals( ClientMessageConst.EVENT_SELECTION ) ) {
String detail = readEventPropertyValue( grid, eventName, EVENT_PARAM_DETAIL );
if( "check".equals( detail ) ) {
String index = readEventPropertyValue( grid, eventName, EVENT_PARAM_INDEX );
item.fireCheckEvent( Integer.valueOf( index ).intValue() );
} else {
item.fireEvent( SWT.Selection );
}
} else {
item.fireEvent( SWT.DefaultSelection );
}
}
}
}
private static boolean hasExpandListener( Grid grid ) {
// Always render listen for Expand and Collapse, currently required for scrollbar
// visibility update and setData events.
return true;
}
private static boolean hasCollapseListener( Grid grid ) {
// Always render listen for Expand and Collapse, currently required for scrollbar
// visibility update and setData events.
return true;
}
/////////////////////////////////
// Process expand/collapse events
private static void processTreeEvent( Grid grid, int eventType, String eventName ) {
if( wasEventSent( grid, eventName ) ) {
String value = readEventPropertyValue( grid, eventName, ClientMessageConst.EVENT_PARAM_ITEM );
Event event = new Event();
event.item = getItem( grid, value );
grid.notifyListeners( eventType, event );
}
}
///////////////
// Item Metrics
private static void renderItemMetrics( Grid grid ) {
ItemMetrics[] itemMetrics = getItemMetrics( grid );
if( WidgetLCAUtil.hasChanged( grid, 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 )
.add( itemMetrics[ i ].checkLeft )
.add( itemMetrics[ i ].checkWidth ) );
}
getRemoteObject( grid ).set( PROP_ITEM_METRICS, metrics );
}
}
static ItemMetrics[] getItemMetrics( Grid grid ) {
int columnCount = grid.getColumnCount();
ItemMetrics[] result = new ItemMetrics[ columnCount ];
IGridAdapter adapter = getGridAdapter( grid );
for( int i = 0; i < columnCount; i++ ) {
result[ i ] = new ItemMetrics();
result[ i ].left = adapter.getCellLeft( i );
result[ i ].width = adapter.getCellWidth( i );
result[ i ].checkLeft = result[ i ].left + adapter.getCheckBoxOffset( i );
result[ i ].checkWidth = adapter.getCheckBoxWidth( i );
result[ i ].imageLeft = result[ i ].left + adapter.getImageOffset( i );
result[ i ].imageWidth = adapter.getImageWidth( i );
result[ i ].textLeft = result[ i ].left + adapter.getTextOffset( i );
result[ i ].textWidth = adapter.getTextWidth( i );
}
return result;
}
private static IGridAdapter getGridAdapter( Grid grid ) {
return grid.getAdapter( IGridAdapter.class );
}
////////////////
// Inner classes
static final class ItemMetrics {
int left;
int width;
int checkLeft;
int checkWidth;
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.checkLeft == checkLeft
&& other.checkWidth == checkWidth
&& 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 );
}
}
}