blob: 9e1c4dfa3e5f356033553b3f80f8249d77d87dc5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013 EclipseSource.
* 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.addons.dropdown.viewer;
import org.eclipse.jface.fieldassist.FieldDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.jface.viewers.*;
import org.eclipse.rap.addons.dropdown.DropDown;
import org.eclipse.rap.addons.dropdown.internal.*;
import org.eclipse.rap.addons.dropdown.viewer.internal.resources.ResourceLoaderUtil;
import org.eclipse.rap.clientscripting.ClientListener;
import org.eclipse.rap.clientscripting.WidgetDataWhiteList;
import org.eclipse.rap.json.JsonObject;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.client.service.JavaScriptLoader;
import org.eclipse.rap.rwt.internal.protocol.JsonUtil;
import org.eclipse.rap.rwt.lifecycle.WidgetUtil;
import org.eclipse.rap.rwt.service.ResourceManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.internal.widgets.ControlDecorator;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Text;
@SuppressWarnings( "restriction" )
public class DropDownViewer extends ContentViewer {
private static final String MODEL_REMOTE_OBJECT_JS
= "rwt/remote/Model.js";
private static final String ATTR_CLIENT_LISTNER_HOLDER
= ClientListenerHolder.class.getName() + "#instance";
private static final String SELECTION_CHANGED = "change:elementSelection";
private static final String VIEWER_LINK = DropDownViewer.class.getName() + "#viewer";
private static final String DROPDOWN_KEY = "dropDown";
private static final String TEXT_KEY = "text";
private static final String DECORATOR_KEY = "decorator";
private static final String ELEMENTS_KEY = "elements";
private static final String SELECTION_KEY = "selection";
private static final boolean USE_NEW_SCRIPTS = true;
private final static String PREFIX
= "org/eclipse/rap/addons/dropdown/viewer/internal/resources/";
private final DropDown dropDown;
private final Text text;
private final ClientListenerHolder clientListeners;
private final Model model = new Model();
private final ModelListener modelSelectionListener = new ModelSelectionListener();
private Object[] elements;
private ControlDecorator decorator;
public DropDownViewer( Text text ) {
this.text = text;
checkParent();
ensureTypeHandler();
dropDown = new DropDown( text );
clientListeners = getClientListenerHolder();
setClientElements( new String[ 0 ] );
createControlDecorator();
attachClientListener();
linkClientObjects();
hookControl( text );
}
@Override
public Control getControl() {
return text;
}
@Override
public ISelection getSelection() {
return null;
}
@Override
public void refresh() {
updateElements();
}
@Override
public void setSelection( ISelection selection, boolean reveal ) {
throw new UnsupportedOperationException( "Setting the selection is currently not supported" );
}
@Override
public void addSelectionChangedListener( ISelectionChangedListener listener ) {
super.addSelectionChangedListener( listener );
model.addListener( SELECTION_CHANGED, modelSelectionListener );
}
/**
* experimental
*/
public void setAutoComplete( boolean value ) {
model.set( "autoComplete", value );
}
@Override
protected void inputChanged( Object input, Object oldInput ) {
updateElements();
}
@Override
protected void handleDispose( DisposeEvent event ) {
super.handleDispose( event );
model.destroy();
}
////////////
// Internals
private void checkParent() {
if( text.getData( VIEWER_LINK ) != null ) {
throw new IllegalStateException( "Text already has a " + getClass().getSimpleName() );
}
}
private void updateElements() {
IStructuredContentProvider contentProvider = ( IStructuredContentProvider )getContentProvider();
Object input = getInput();
if( contentProvider != null && input != null ) {
elements = contentProvider.getElements( input );
updateClientElements();
}
}
private void updateClientElements() {
ILabelProvider labelProvider = ( ILabelProvider )getLabelProvider();
if( elements != null ) {
String[] clientElements = new String[ elements.length ];
for( int i = 0; i < elements.length; i++ ) {
clientElements[ i ] = labelProvider.getText( elements[ i ] );
}
setClientElements( clientElements );
}
}
private void setClientElements( String[] elements ) {
// TODO : Using a separate client object (e.g. "RemoteList") for the elements might allow
// sharing and incremental updates
model.set( ELEMENTS_KEY, JsonUtil.createJsonArray( elements ) );
model.set( SELECTION_KEY, -1 );
model.notify( "refresh" );
}
private void createControlDecorator() {
decorator = new ControlDecorator( text, SWT.LEFT | SWT.TOP, null );
decorator.setMarginWidth( 2 );
decorator.setImage( getDecorationImage( FieldDecorationRegistry.DEC_ERROR ) );
decorator.hide();
}
private static Image getDecorationImage( String id ) {
FieldDecorationRegistry registry = FieldDecorationRegistry.getDefault();
FieldDecoration decoration = registry.getFieldDecoration( id );
return decoration.getImage();
}
private void attachClientListener() {
if( USE_NEW_SCRIPTS ) {
text.addListener( SWT.Modify, getClientListener( "DataBinding.js" ) );
text.addListener( SWT.Verify, getClientListener( "DataBinding.js" ) );
dropDown.addListener( SWT.Selection, getClientListener( "DataBinding.js" ) );
dropDown.addListener( SWT.DefaultSelection, getClientListener( "DataBinding.js" ) );
model.addListener( "change", getModelListener( "DataBinding.js" ) );
model.addListener( "change", getModelListener( "ModelListener.js" ) );
model.addListener( "accept", getModelListener( "ModelListener.js" ) );
} else {
text.addListener( ClientListener.Modify, getTextModifyListener() );
text.addListener( ClientListener.Verify, getTextVerifyListener() );
text.addListener( ClientListener.KeyDown, getTextKeyDownListener() );
text.addListener( ClientListener.MouseDown, getTextMouseDownListener() );
dropDown.addListener( ClientListener.Selection, getDropDownSelectionListener() );
dropDown.addListener( ClientListener.DefaultSelection, getDropDownDefaultSelectionListener() );
dropDown.addListener( ClientListener.Show, getDropDownShowListener() );
dropDown.addListener( ClientListener.Hide, getDropDownHideListener() );
model.addListener( "refresh", getRefreshListener() );
}
}
public ClientListener getClientListener( String name ) {
return new ClientListener( ResourceLoaderUtil.readTextContent( PREFIX + name ) );
}
public ClientModelListener getModelListener( String name ) {
return new ClientModelListener( ResourceLoaderUtil.readTextContent( PREFIX + name ) );
}
private void linkClientObjects() {
// NOTE : Order is relevant, DropDown renders immediately!
WidgetDataWhiteList.addKey( VIEWER_LINK );
text.setData( VIEWER_LINK, model.getId() );
dropDown.setData( VIEWER_LINK, model.getId() );
model.set( USE_NEW_SCRIPTS ? "dropDownWidgetId" : DROPDOWN_KEY, WidgetUtil.getId( dropDown ) );
model.set( USE_NEW_SCRIPTS ? "textWidgetId" : TEXT_KEY, WidgetUtil.getId( text ) );
model.set( USE_NEW_SCRIPTS ? "decoratorWidgetId" : DECORATOR_KEY, WidgetUtil.getId( decorator ) );
}
Model getModel() {
return model;
}
ClientListener getTextModifyListener() {
return clientListeners.getTextModifyListener();
}
ClientListener getTextVerifyListener() {
return clientListeners.getTextVerifyListener();
}
ClientListener getTextKeyDownListener() {
return clientListeners.getTextKeyDownListener();
}
ClientListener getTextMouseDownListener() {
return clientListeners.getTextMouseDownListener();
}
ClientModelListener getRefreshListener() {
return clientListeners.getRefreshListener();
}
DropDown getDropDown() {
return dropDown;
}
ControlDecorator getDecorator() {
return decorator;
}
ClientListener getDropDownSelectionListener() {
return clientListeners.getDropDownSelectionListener();
}
ClientListener getDropDownDefaultSelectionListener() {
return clientListeners.getDropDownDefaultSelectionListener();
}
ClientListener getDropDownShowListener() {
return clientListeners.getDropDownShowListener();
}
ClientListener getDropDownHideListener() {
return clientListeners.getDropDownHideListener();
}
// TODO : no longer planned to be replaceable, streamline.
private ClientListenerHolder getClientListenerHolder() {
Object result = RWT.getUISession().getAttribute( ATTR_CLIENT_LISTNER_HOLDER );
if( result == null ) {
result = new ClientListenerHolder();
RWT.getUISession().setAttribute( ATTR_CLIENT_LISTNER_HOLDER, result );
}
return ( ClientListenerHolder )result;
}
private void fireSelectionChanged( int index ) {
Object element = elements[ index ];
ISelection selection = new StructuredSelection( element );
fireSelectionChanged( new SelectionChangedEvent( this, selection ) );
}
private void ensureTypeHandler() {
ResourceManager manager = RWT.getResourceManager();
if( !manager.isRegistered( MODEL_REMOTE_OBJECT_JS ) ) {
manager.register(
MODEL_REMOTE_OBJECT_JS,
getClass().getClassLoader().getResourceAsStream( MODEL_REMOTE_OBJECT_JS )
);
}
JavaScriptLoader jsl = RWT.getClient().getService( JavaScriptLoader.class );
jsl.require( manager.getLocation( MODEL_REMOTE_OBJECT_JS ) );
}
private class ModelSelectionListener implements ModelListener {
public void handleEvent( JsonObject properties ) {
int index = properties.get( "value" ).asInt();
fireSelectionChanged( index );
}
}
private static class ClientListenerHolder {
private final ClientListener textListener = createListener( "TextEventListener.js" );
private final ClientListener dropDownListener = createListener( "DropDownEventListener.js" );
private final ClientModelListener refreshListener
= createModelListener( "RefreshListener.js" );
private ClientListener createListener( String name ) {
return new ClientListener( ResourceLoaderUtil.readTextContent( PREFIX + name ) );
}
private ClientModelListener createModelListener( String name ) {
return new ClientModelListener( ResourceLoaderUtil.readTextContent( PREFIX + name ) );
}
public ClientModelListener getRefreshListener() {
return refreshListener;
}
public ClientListener getDropDownShowListener() {
return dropDownListener;
}
public ClientListener getDropDownHideListener() {
return dropDownListener;
}
public ClientListener getTextModifyListener() {
return textListener;
}
public ClientListener getDropDownDefaultSelectionListener() {
return dropDownListener;
}
public ClientListener getDropDownSelectionListener() {
return dropDownListener;
}
public ClientListener getTextKeyDownListener() {
return textListener;
}
public ClientListener getTextMouseDownListener() {
return textListener;
}
public ClientListener getTextVerifyListener() {
return textListener;
}
}
}