/*******************************************************************************
* Copyright (c) 2002, 2010 Innoopract Informationssysteme GmbH.
* 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.combokit;
import java.io.IOException;
import org.eclipse.rwt.graphics.Graphics;
import org.eclipse.rwt.internal.util.EncodingUtil;
import org.eclipse.rwt.lifecycle.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.internal.widgets.Props;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Widget;
/**
* Life cycle adapter for Combo widgets.
*/
public class ComboLCA extends AbstractWidgetLCA {
private static final String[] DEFAUT_ITEMS = new String[ 0 ];
private static final Integer DEFAULT_SELECTION = new Integer( -1 );
private static final Point DEFAULT_TEXT_SELECTION = new Point( 0, 0 );
private static final Integer DEFAULT_TEXT_LIMIT = new Integer( Combo.LIMIT );
// Must be in sync with appearance "list-item"
private static final int LIST_ITEM_PADDING = 3;
// 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 ) {
Combo combo = ( Combo )widget;
ControlLCAUtil.preserveValues( combo );
IWidgetAdapter adapter = WidgetUtil.getAdapter( widget );
String[] items = combo.getItems();
adapter.preserve( PROP_ITEMS, items );
Integer selection = new Integer( combo.getSelectionIndex() );
adapter.preserve( PROP_SELECTION, selection );
adapter.preserve( PROP_TEXT_SELECTION, combo.getSelection() );
adapter.preserve( PROP_TEXT_LIMIT,
new Integer( combo.getTextLimit() ) );
adapter.preserve( PROP_MAX_LIST_HEIGHT,
new Integer( getMaxListHeight( combo ) ) );
adapter.preserve( PROP_LIST_ITEM_HEIGHT,
new Integer( getListItemHeight( combo ) ) );
adapter.preserve( PROP_TEXT, combo.getText() );
adapter.preserve( Props.SELECTION_LISTENERS,
Boolean.valueOf( SelectionEvent.hasListener( combo ) ) );
adapter.preserve( PROP_LIST_VISIBLE,
new Boolean( combo.getListVisible() ) );
adapter.preserve( PROP_EDITABLE, Boolean.valueOf( isEditable( combo ) ) );
boolean hasVerifyListener = VerifyEvent.hasListener( combo );
boolean hasModifyListener = ModifyEvent.hasListener( combo );
boolean hasListener = hasVerifyListener || hasModifyListener;
adapter.preserve( PROP_VERIFY_MODIFY_LISTENER,
Boolean.valueOf( hasListener ) );
WidgetLCAUtil.preserveCustomVariant( combo );
}
public void readData( final Widget widget ) {
final Combo combo = ( Combo )widget;
String value = WidgetLCAUtil.readPropertyValue( widget, "selectedItem" );
if( value != null ) {
combo.select( Integer.parseInt( value ) );
}
String listVisible
= WidgetLCAUtil.readPropertyValue( combo, "listVisible" );
if( listVisible != null ) {
combo.setListVisible( Boolean.valueOf( listVisible ).booleanValue() );
}
readTextAndSelection( combo );
ControlLCAUtil.processSelection( combo, null, true );
ControlLCAUtil.processMouseEvents( combo );
ControlLCAUtil.processKeyEvents( combo );
ControlLCAUtil.processMenuDetect( combo );
WidgetLCAUtil.processHelp( combo );
}
public void renderInitialization( final Widget widget ) throws IOException {
Combo combo = ( Combo )widget;
JSWriter writer = JSWriter.getWriterFor( widget );
writer.newWidget( "org.eclipse.swt.widgets.Combo" );
ControlLCAUtil.writeStyleFlags( combo );
}
public void renderChanges( final Widget widget ) throws IOException {
Combo combo = ( Combo )widget;
ControlLCAUtil.writeChanges( combo );
writeListItemHeight( combo );
writeItems( combo );
writeSelectionListener( combo );
writeSelection( combo );
writeMaxListHeight( combo );
writeEditable( combo );
writeText( combo );
writeTextSelection( combo );
writeListVisible( combo );
writeTextLimit( combo );
writeVerifyAndModifyListener( combo );
WidgetLCAUtil.writeCustomVariant( combo );
}
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 Combo combo ) {
final Point selection = readSelection( combo );
final String value = WidgetLCAUtil.readPropertyValue( combo, "text" );
if( value != null ) {
if( VerifyEvent.hasListener( combo ) ) {
// 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() {
combo.setText( value );
// since text is set in process action, preserved values have to be
// replaced
IWidgetAdapter adapter = WidgetUtil.getAdapter( combo );
adapter.preserve( PROP_TEXT, value );
if( selection != null ) {
combo.setSelection( selection );
adapter.preserve( PROP_TEXT_SELECTION, selection );
}
}
} );
} else {
combo.setText( value );
if( selection != null ) {
combo.setSelection( selection );
}
}
} else if( selection != null ) {
combo.setSelection( selection );
}
}
private static Point readSelection( final Combo combo ) {
Point result = null;
String selStart = WidgetLCAUtil.readPropertyValue( combo,
"selectionStart" );
String selLength = WidgetLCAUtil.readPropertyValue( combo,
"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 Combo combo ) throws IOException {
JSWriter writer = JSWriter.getWriterFor( combo );
String[] items = combo.getItems();
if( WidgetLCAUtil.hasChanged( combo, 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 Combo combo ) throws IOException {
Integer newValue = new Integer( combo.getSelectionIndex() );
Integer defValue = DEFAULT_SELECTION;
boolean selectionChanged
= WidgetLCAUtil.hasChanged( combo, PROP_SELECTION, newValue, defValue );
// The 'textChanged' statement covers the following use case:
// combo.add( "a" ); combo.select( 0 );
// -- in a subsequent request --
// combo.removeAll(); combo.add( "b" ); combo.select( 0 );
// When only examining selectionIndex, a change cannot be determined
boolean textChanged = !isEditable( combo )
&& WidgetLCAUtil.hasChanged( combo,
PROP_TEXT,
combo.getText(),
"" );
if( selectionChanged || textChanged ) {
JSWriter writer = JSWriter.getWriterFor( combo );
writer.call( JS_FUNC_SELECT, new Object[] { newValue } );
}
}
private static void writeTextSelection( final Combo combo )
throws IOException
{
Point newValue = combo.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( combo,
PROP_TEXT_SELECTION,
newValue,
defValue ) ) {
// [rh] Workaround for bug 252462: Changing selection on a hidden text
// widget causes exception in FF
if( combo.isVisible() ) {
JSWriter writer = JSWriter.getWriterFor( combo );
writer.call( JS_FUNC_SET_SELECTION_TEXT,
new Object[] { start, count } );
}
}
}
private static void writeTextLimit( final Combo combo )
throws IOException
{
JSWriter writer = JSWriter.getWriterFor( combo );
Integer newValue = new Integer( combo.getTextLimit() );
Integer defValue = DEFAULT_TEXT_LIMIT;
if( WidgetLCAUtil.hasChanged( combo,
PROP_TEXT_LIMIT,
newValue,
defValue ) )
{
if( newValue.intValue() == Combo.LIMIT ) {
newValue = null;
}
writer.set( "textLimit", newValue );
}
}
private static void writeListItemHeight( final Combo combo )
throws IOException
{
Integer newValue = new Integer( getListItemHeight( combo ) );
if( WidgetLCAUtil.hasChanged( combo,
PROP_LIST_ITEM_HEIGHT,
newValue ) )
{
JSWriter writer = JSWriter.getWriterFor( combo );
writer.set( PROP_LIST_ITEM_HEIGHT, "listItemHeight", newValue );
}
}
private static void writeMaxListHeight( final Combo combo )
throws IOException
{
Integer newValue = new Integer( getMaxListHeight( combo ) );
if( WidgetLCAUtil.hasChanged( combo,
PROP_MAX_LIST_HEIGHT,
newValue ) )
{
JSWriter writer = JSWriter.getWriterFor( combo );
writer.set( PROP_MAX_LIST_HEIGHT, "maxListHeight", newValue );
}
}
private static void writeListVisible( final Combo combo )
throws IOException
{
Boolean newValue = Boolean.valueOf( combo.getListVisible() );
JSWriter writer = JSWriter.getWriterFor( combo );
writer.set( PROP_LIST_VISIBLE, "listVisible", newValue, Boolean.FALSE );
}
private static void writeEditable( final Combo combo ) throws IOException {
boolean editable = isEditable( combo );
Boolean newValue = Boolean.valueOf( editable );
JSWriter writer = JSWriter.getWriterFor( combo );
writer.set( PROP_EDITABLE, "editable", newValue, Boolean.TRUE );
}
private static void writeText( final Combo combo ) throws IOException {
if( isEditable( combo ) ) {
String newValue = combo.getText();
JSWriter writer = JSWriter.getWriterFor( combo );
if( WidgetLCAUtil.hasChanged( combo, PROP_TEXT, newValue, "" ) ) {
String value = EncodingUtil.removeNonDisplayableChars( newValue );
writer.set( "value", value );
}
}
}
private static void writeSelectionListener( final Combo combo )
throws IOException
{
boolean hasListener = SelectionEvent.hasListener( combo );
Boolean newValue = Boolean.valueOf( hasListener );
String prop = Props.SELECTION_LISTENERS;
if( WidgetLCAUtil.hasChanged( combo, prop, newValue, Boolean.FALSE ) ) {
JSWriter writer = JSWriter.getWriterFor( combo );
writer.set( "hasSelectionListener", newValue );
}
}
private static void writeVerifyAndModifyListener( final Combo combo )
throws IOException
{
boolean hasVerifyListener = VerifyEvent.hasListener( combo );
boolean hasModifyListener = ModifyEvent.hasListener( combo );
boolean hasListener = hasVerifyListener || hasModifyListener;
Boolean newValue = Boolean.valueOf( hasListener );
String prop = PROP_VERIFY_MODIFY_LISTENER;
if( WidgetLCAUtil.hasChanged( combo, prop, newValue, Boolean.FALSE ) ) {
JSWriter writer = JSWriter.getWriterFor( combo );
writer.set( "hasVerifyModifyListener", newValue );
}
}
//////////////////
// Helping methods
private static boolean isEditable( final Combo combo ) {
return ( ( combo.getStyle() & SWT.READ_ONLY ) == 0 );
}
static int getMaxListHeight( final Combo combo ) {
int visibleItemCount = combo.getVisibleItemCount();
int itemHeight = getListItemHeight( combo );
return visibleItemCount * itemHeight;
}
static int getListItemHeight( final Combo combo ) {
int charHeight = Graphics.getCharHeight( combo.getFont() );
int padding = 2 * LIST_ITEM_PADDING;
return charHeight + padding;
}
}