/*******************************************************************************
* Copyright (c) 2009, 2010 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.swt.internal.custom.ccombokit;
import java.io.IOException;
import org.eclipse.rwt.internal.util.EncodingUtil;
import org.eclipse.rwt.lifecycle.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.internal.widgets.Props;
import org.eclipse.swt.widgets.Widget;
public final class CComboLCA extends AbstractWidgetLCA {
private static final String[] DEFAUT_ITEMS = new String[ 0 ];
private static final Integer DEFAULT_SELECTION = new Integer( -1 );
private static final Integer DEFAULT_TEXT_LIMIT = new Integer( CCombo.LIMIT );
private static final Point DEFAULT_TEXT_SELECTION = new Point( 0, 0 );
// Constants for JS functions names
private static final String JS_FUNC_SELECT = "select";
private static final String JS_FUNC_SET_SELECTION_TEXT = "setTextSelection";
// Property names for preserve-value facility
static final String PROP_ITEMS = "items";
static final String PROP_TEXT = "text";
static final String PROP_SELECTION = "selection";
static final String PROP_TEXT_SELECTION = "textSelection";
static final String PROP_TEXT_LIMIT = "textLimit";
static final String PROP_LIST_VISIBLE = "listVisible";
static final String PROP_EDITABLE = "editable";
static final String PROP_VERIFY_MODIFY_LISTENER = "verifyModifyListener";
static final String PROP_MAX_LIST_HEIGHT = "maxListHeight";
static final String PROP_LIST_ITEM_HEIGHT = "listItemHeight";
public void preserveValues( final Widget widget ) {
CCombo ccombo = ( CCombo )widget;
ControlLCAUtil.preserveValues( ccombo );
IWidgetAdapter adapter = WidgetUtil.getAdapter( widget );
String[] items = ccombo.getItems();
adapter.preserve( PROP_ITEMS, items );
Integer selection = new Integer( ccombo.getSelectionIndex() );
adapter.preserve( PROP_SELECTION, selection );
adapter.preserve( PROP_TEXT_SELECTION, ccombo.getSelection() );
adapter.preserve( PROP_TEXT_LIMIT,
new Integer( ccombo.getTextLimit() ) );
adapter.preserve( PROP_MAX_LIST_HEIGHT,
new Integer( getMaxListHeight( ccombo ) ) );
adapter.preserve( PROP_LIST_ITEM_HEIGHT,
new Integer( ccombo.getItemHeight() ) );
adapter.preserve( PROP_TEXT, ccombo.getText() );
adapter.preserve( Props.SELECTION_LISTENERS,
Boolean.valueOf( SelectionEvent.hasListener( ccombo ) ) );
adapter.preserve( PROP_LIST_VISIBLE,
new Boolean( ccombo.getListVisible() ) );
adapter.preserve( PROP_EDITABLE, Boolean.valueOf( isEditable( ccombo ) ) );
boolean hasVerifyListener = VerifyEvent.hasListener( ccombo );
boolean hasModifyListener = ModifyEvent.hasListener( ccombo );
boolean hasListener = hasVerifyListener || hasModifyListener;
adapter.preserve( PROP_VERIFY_MODIFY_LISTENER,
Boolean.valueOf( hasListener ) );
WidgetLCAUtil.preserveCustomVariant( ccombo );
}
public void readData( final Widget widget ) {
final CCombo ccombo = ( CCombo )widget;
String value = WidgetLCAUtil.readPropertyValue( ccombo, "selectedItem" );
if( value != null ) {
ccombo.select( Integer.parseInt( value ) );
}
String listVisible
= WidgetLCAUtil.readPropertyValue( ccombo, "listVisible" );
if( listVisible != null ) {
ccombo.setListVisible( Boolean.valueOf( listVisible ).booleanValue() );
}
readTextAndSelection( ccombo );
ControlLCAUtil.processSelection( ccombo, null, true );
ControlLCAUtil.processMouseEvents( ccombo );
ControlLCAUtil.processKeyEvents( ccombo );
ControlLCAUtil.processMenuDetect( ccombo );
WidgetLCAUtil.processHelp( ccombo );
}
public void renderInitialization( final Widget widget ) throws IOException {
CCombo ccombo = ( CCombo )widget;
JSWriter writer = JSWriter.getWriterFor( widget );
writer.newWidget( "org.eclipse.swt.widgets.Combo",
new Object[] { "ccombo" } );
ControlLCAUtil.writeStyleFlags( ccombo );
WidgetLCAUtil.writeStyleFlag( ccombo, SWT.FLAT, "FLAT" );
}
public void renderChanges( final Widget widget ) throws IOException {
CCombo ccombo = ( CCombo )widget;
ControlLCAUtil.writeChanges( ccombo );
writeListItemHeight( ccombo );
writeItems( ccombo );
writeSelection( ccombo );
writeMaxListHeight( ccombo );
writeEditable( ccombo );
writeText( ccombo );
writeTextSelection( ccombo );
writeListVisible( ccombo );
writeTextLimit( ccombo );
writeSelectionListener( ccombo );
writeVerifyAndModifyListener( ccombo );
WidgetLCAUtil.writeCustomVariant( ccombo );
}
public void renderDispose( final Widget widget ) throws IOException {
JSWriter writer = JSWriter.getWriterFor( widget );
writer.dispose();
}
///////////////////////////////////////
// Helping methods to read client state
private static void readTextAndSelection( final CCombo ccombo ) {
final Point selection = readSelection( ccombo );
final String txt = WidgetLCAUtil.readPropertyValue( ccombo, "text" );
if( txt != null ) {
if( VerifyEvent.hasListener( ccombo ) ) {
// setText needs to be executed in a ProcessAcction runnable as it may
// fire a VerifyEvent whose fields (text and doit) need to be evaluated
// before actually setting the new value
ProcessActionRunner.add( new Runnable() {
public void run() {
ccombo.setText( txt );
// since text is set in process action, preserved values have to be
// replaced
IWidgetAdapter adapter = WidgetUtil.getAdapter( ccombo );
adapter.preserve( PROP_TEXT, txt );
if( selection != null ) {
ccombo.setSelection( selection );
adapter.preserve( PROP_TEXT_SELECTION, selection );
}
}
} );
} else {
ccombo.setText( txt );
if( selection != null ) {
ccombo.setSelection( selection );
}
}
} else if( selection != null ) {
ccombo.setSelection( selection );
}
}
private static Point readSelection( final CCombo ccombo ) {
Point result = null;
String selStart = WidgetLCAUtil.readPropertyValue( ccombo,
"selectionStart" );
String selLength = WidgetLCAUtil.readPropertyValue( ccombo,
"selectionLength" );
if( selStart != null || selLength != null ) {
result = new Point( 0, 0 );
if( selStart != null ) {
result.x = Integer.parseInt( selStart );
}
if( selLength != null ) {
result.y = result.x + Integer.parseInt( selLength );
}
}
return result;
}
//////////////////////////////////////////////
// Helping methods to write changed properties
private static void writeItems( final CCombo ccombo ) throws IOException {
JSWriter writer = JSWriter.getWriterFor( ccombo );
String[] items = ccombo.getItems();
if( WidgetLCAUtil.hasChanged( ccombo, PROP_ITEMS, items, DEFAUT_ITEMS ) ) {
// Convert newlines into whitespaces
for( int i = 0; i < items.length; i++ ) {
items[ i ] = WidgetLCAUtil.replaceNewLines( items[ i ], " " );
items[ i ] = WidgetLCAUtil.escapeText( items[ i ], false );
items[ i ] = EncodingUtil.replaceWhiteSpaces( items[ i ] );
}
writer.set( PROP_ITEMS, new Object[] { items } );
}
}
private static void writeSelection( final CCombo ccombo )
throws IOException
{
Integer newValue = new Integer( ccombo.getSelectionIndex() );
Integer defValue = DEFAULT_SELECTION;
boolean selectionChanged = WidgetLCAUtil.hasChanged( ccombo,
PROP_SELECTION,
newValue,
defValue );
// The 'textChanged' statement covers the following use case:
// ccombo.add( "a" ); ccombo.select( 0 );
// -- in a subsequent request --
// ccombo.removeAll(); ccombo.add( "b" ); ccombo.select( 0 );
// When only examining selectionIndex, a change cannot be determined
boolean textChanged = !isEditable( ccombo )
&& WidgetLCAUtil.hasChanged( ccombo,
PROP_TEXT,
ccombo.getText(),
"" );
if( selectionChanged || textChanged ) {
JSWriter writer = JSWriter.getWriterFor( ccombo );
writer.call( JS_FUNC_SELECT, new Object[] { newValue } );
}
}
private static void writeTextSelection( final CCombo ccombo )
throws IOException
{
Point newValue = ccombo.getSelection();
Point defValue = DEFAULT_TEXT_SELECTION;
Integer start = new Integer( newValue.x );
Integer end = new Integer( newValue.y );
Integer count = new Integer( end.intValue() - start.intValue() );
// TODO [rh] could be optimized: when text was changed and selection is 0,0
// there is no need to write JavaScript since the client resets the
// selection as well when the new text is set.
if( WidgetLCAUtil.hasChanged( ccombo,
PROP_TEXT_SELECTION,
newValue,
defValue ) ) {
// [rh] Workaround for bug 252462: Changing selection on a hidden text
// widget causes exception in FF
if( ccombo.isVisible() ) {
JSWriter writer = JSWriter.getWriterFor( ccombo );
writer.call( JS_FUNC_SET_SELECTION_TEXT,
new Object[] { start, count } );
}
}
}
private static void writeTextLimit( final CCombo ccombo )
throws IOException
{
JSWriter writer = JSWriter.getWriterFor( ccombo );
Integer newValue = new Integer( ccombo.getTextLimit() );
Integer defValue = DEFAULT_TEXT_LIMIT;
if( WidgetLCAUtil.hasChanged( ccombo,
PROP_TEXT_LIMIT,
newValue,
defValue ) )
{
if( newValue.intValue() == CCombo.LIMIT ) {
newValue = null;
}
writer.set( "textLimit", newValue );
}
}
private static void writeListItemHeight( final CCombo ccombo )
throws IOException
{
Integer newValue = new Integer( ccombo.getItemHeight() );
if( WidgetLCAUtil.hasChanged( ccombo,
PROP_LIST_ITEM_HEIGHT,
newValue ) )
{
JSWriter writer = JSWriter.getWriterFor( ccombo );
writer.set( PROP_LIST_ITEM_HEIGHT, "listItemHeight", newValue );
}
}
private static void writeMaxListHeight( final CCombo ccombo )
throws IOException
{
Integer newValue = new Integer( getMaxListHeight( ccombo ) );
if( WidgetLCAUtil.hasChanged( ccombo,
PROP_MAX_LIST_HEIGHT,
newValue ) )
{
JSWriter writer = JSWriter.getWriterFor( ccombo );
writer.set( PROP_MAX_LIST_HEIGHT, "maxListHeight", newValue );
}
}
private static void writeListVisible( final CCombo ccombo )
throws IOException
{
boolean listVisible = ccombo.getListVisible();
Boolean newValue = Boolean.valueOf( listVisible );
if( WidgetLCAUtil.hasChanged( ccombo, PROP_LIST_VISIBLE, newValue ) ) {
JSWriter writer = JSWriter.getWriterFor( ccombo );
writer.set( PROP_LIST_VISIBLE, "listVisible", newValue, null );
}
}
private static void writeEditable( final CCombo ccombo )
throws IOException
{
boolean editable = isEditable( ccombo );
Boolean newValue = Boolean.valueOf( editable );
if( WidgetLCAUtil.hasChanged( ccombo, PROP_EDITABLE, newValue ) ) {
JSWriter writer = JSWriter.getWriterFor( ccombo );
writer.set( PROP_EDITABLE, "editable", newValue, null );
}
}
private static void writeText( final CCombo ccombo ) throws IOException {
if( isEditable( ccombo ) || ccombo.getSelectionIndex() == -1 ) {
String newValue = ccombo.getText();
JSWriter writer = JSWriter.getWriterFor( ccombo );
if( WidgetLCAUtil.hasChanged( ccombo, PROP_TEXT, newValue, "" ) ) {
String value = EncodingUtil.removeNonDisplayableChars( newValue );
writer.set( "value", value );
}
}
}
private static void writeSelectionListener( final CCombo ccombo )
throws IOException
{
boolean hasListener = SelectionEvent.hasListener( ccombo );
Boolean newValue = Boolean.valueOf( hasListener );
String prop = Props.SELECTION_LISTENERS;
if( WidgetLCAUtil.hasChanged( ccombo, prop, newValue, Boolean.FALSE ) ) {
JSWriter writer = JSWriter.getWriterFor( ccombo );
writer.set( "hasSelectionListener", newValue );
}
}
private static void writeVerifyAndModifyListener( final CCombo ccombo )
throws IOException
{
boolean hasVerifyListener = VerifyEvent.hasListener( ccombo );
boolean hasModifyListener = ModifyEvent.hasListener( ccombo );
boolean hasListener = hasVerifyListener || hasModifyListener;
Boolean newValue = Boolean.valueOf( hasListener );
String prop = PROP_VERIFY_MODIFY_LISTENER;
if( WidgetLCAUtil.hasChanged( ccombo, prop, newValue, Boolean.FALSE ) ) {
JSWriter writer = JSWriter.getWriterFor( ccombo );
writer.set( "hasVerifyModifyListener", newValue );
}
}
//////////////////
// Helping methods
private static boolean isEditable( final CCombo ccombo ) {
return ( ( ccombo.getStyle() & SWT.READ_ONLY ) == 0 );
}
static int getMaxListHeight( final CCombo ccombo ) {
int visibleItemCount = ccombo.getVisibleItemCount();
int itemHeight = ccombo.getItemHeight();
return visibleItemCount * itemHeight;
}
}