/******************************************************************************* * Copyright (c) 2014 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 com.eclipsesource.tabris.widgets; import static com.eclipsesource.tabris.internal.Clauses.when; import static com.eclipsesource.tabris.internal.Clauses.whenNull; import static com.eclipsesource.tabris.internal.Constants.EVENT_CLIENT_DIALOG_CLOSE; import static com.eclipsesource.tabris.internal.Constants.EVENT_SELECTION; import static com.eclipsesource.tabris.internal.Constants.METHOD_CLOSE; import static com.eclipsesource.tabris.internal.Constants.METHOD_OPEN; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_BUTTON_CANCEL; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_BUTTON_NEUTRAL; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_BUTTON_OK; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_BUTTON_TYPE; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_MESSAGE; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_SEVERITY; import static com.eclipsesource.tabris.internal.Constants.PROPERTY_TITLE; import static com.eclipsesource.tabris.internal.Constants.TYPE_CLIENT_DIALOG; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.rap.json.JsonObject; import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.internal.lifecycle.ProcessActionRunner; import org.eclipse.rap.rwt.remote.AbstractOperationHandler; import org.eclipse.rap.rwt.remote.RemoteObject; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import com.eclipsesource.tabris.internal.Constants; /** * <p> * A {@link ClientDialog} is a client side dialog. A client uses native dialogs to visualize the {@link ClientDialog}. * For this reason dialogs may vary form client to client. * </p> * * @since 1.4 */ @SuppressWarnings("restriction") public class ClientDialog implements Serializable { /** * <p> * A client dialog can have three buttons. An OK, CANCEL and a neutral button. * </p> */ public static enum ButtonType { /** * <p> * An OK button will be shown where native clients show ok or positive buttons. * </p> */ OK, /** * <p> * A CANCEL button will be shown where native clients show cancel or negative buttons. * </p> */ CANCEL, /** * <p> * A NEUTRAL button will be shown where native clients show additional or neutral buttons. * </p> */ NEUTRAL } /** * <p> * A {@link ClientDialog} can have a severity. Some clients interpret the severity by showing a severity icon. * </p> */ public static enum Severity { WARNING, ERROR } private final RemoteObject remoteObject; private final Map<ButtonType, Listener> buttons; private final List<ClientDialogListener> dialogListeners; private String title; private String message; private Severity severity; public ClientDialog() { this.remoteObject = RWT.getUISession().getConnection().createRemoteObject( TYPE_CLIENT_DIALOG ); this.remoteObject.setHandler( new DialogOperationHandler() ); this.buttons = new HashMap<ButtonType, Listener>(); this.dialogListeners = new ArrayList<ClientDialogListener>(); } /** * <p> * Sets the title of a {@link ClientDialog}. The title may be displayed more emphasized as normal text. * </p> * * @param title the title to show. Must not be <code>null</code>. */ public void setTitle( String title ) { whenNull( title ).throwIllegalArgument( "title must not be null" ); this.remoteObject.set( PROPERTY_TITLE, title ); this.title = title; } public String getTitle() { return title; } /** * <p> * Sets the message of a {@link ClientDialog}. Usually the message will be displayed as a body of the native dialog. * </p> * * @param message the message to display. Must not be <code>null</code>. */ public ClientDialog setMessage( String message ) { whenNull( message ).throwIllegalArgument( "message must not be null" ); this.message = message; this.remoteObject.set( PROPERTY_MESSAGE, message ); return this; } public String getMessage() { return message; } /** * <p> * Sets the {@link Severity} of a {@link ClientDialog}. * </p> * * @param severity the {@link Severity} to use. Must not be <code>null</code>. */ public ClientDialog setSeverity( Severity severity ) { whenNull( severity ).throwIllegalArgument( "severity must not be null" ); this.severity = severity; remoteObject.set( PROPERTY_SEVERITY, severity.toString() ); return this; } public Severity getSeverity() { return severity; } /** * <p> * Sets a {@link ClientDialog} button with the specified {@link ButtonType}. The client is responsible to position * the button on its right place. * </p> * * @param type the {@link ButtonType} of the button. Must not be <code>null</code>. * @param text the text of the button. Must not be <code>null</code> or empty. */ public ClientDialog setButton( ButtonType type, String text ) { return setButton( type, text, null ); } /** * <p> * Sets a {@link ClientDialog} button with the specified {@link ButtonType}. The client is responsible to position * the button on its right place. When the button was pressed the {@link Listener} will be notified. * </p> * * @param type the {@link ButtonType} of the button. Must not be <code>null</code>. * @param text the text of the button. Must not be <code>null</code> or empty. * @param listener the listener to notify when the button was pressed. May be <code>null</code>. */ public ClientDialog setButton( ButtonType type, String text, Listener listener ) { whenNull( type ).throwIllegalArgument( "type must not be null" ); whenNull( text ).throwIllegalArgument( "text must not be null" ); when( text.isEmpty() ).throwIllegalArgument( "text must not be empty" ); remoteObject.set( createButtonKey( type ), text ); addListener( type, listener ); return this; } private String createButtonKey( ButtonType type ) { switch( type ) { case OK: return PROPERTY_BUTTON_OK; case CANCEL: return PROPERTY_BUTTON_CANCEL; case NEUTRAL: return PROPERTY_BUTTON_NEUTRAL; default: return null; } } private void addListener( ButtonType type, Listener listener ) { if( listener != null ) { if( buttons.isEmpty() ) { remoteObject.listen( EVENT_SELECTION, true ); } buttons.put( type, listener ); } } /** * <p> * Instructs a client to open the {@link ClientDialog}. * </p> */ public void open() { remoteObject.call( METHOD_OPEN, null ); notifyOpenListeners(); } private void notifyOpenListeners() { final List<ClientDialogListener> listeners = new ArrayList<ClientDialogListener>( dialogListeners ); ProcessActionRunner.add( new Runnable() { @Override public void run() { for( ClientDialogListener listener : listeners ) { listener.open(); } } } ); } /** * <p> * Instructs a client to close a {@link ClientDialog}. * </p> */ public void close() { remoteObject.call( METHOD_CLOSE, null ); } /** * <p> * Adds a {@link ClientDialogListener} that will be notified when a dialog was opened or closed. * </p> * * @param listener the listener to add. Must not be <code>null</code>. */ public void addClientDialogListener( ClientDialogListener listener ) { whenNull( listener ).throwIllegalArgument( "listener must not be null" ); if( dialogListeners.isEmpty() ) { remoteObject.listen( EVENT_CLIENT_DIALOG_CLOSE, true ); } dialogListeners.add( listener ); } /** * <p> * Removes a {@link ClientDialogListener}. * </p> * * @param listener the listener to add. Must not be <code>null</code>. */ public void removeClientDialogListener( ClientDialogListener listener ) { whenNull( listener ).throwIllegalArgument( "listener must not be null" ); dialogListeners.remove( listener ); if( dialogListeners.isEmpty() ) { remoteObject.listen( EVENT_CLIENT_DIALOG_CLOSE, false ); } } private class DialogOperationHandler extends AbstractOperationHandler { @Override public void handleNotify( String event, JsonObject properties ) { if( event.equals( Constants.EVENT_SELECTION ) ) { dispatchSelectionEvent( properties ); } else if( event.equals( EVENT_CLIENT_DIALOG_CLOSE ) ) { dispatchCloseEvent(); } } } private void dispatchSelectionEvent( JsonObject properties ) { String type = properties.get( PROPERTY_BUTTON_TYPE ).asString(); Listener listener = buttons.get( getButtonType( type ) ); if( listener != null ) { notifySelectionListener( listener ); } } private ButtonType getButtonType( String type ) { if( type.equals( PROPERTY_BUTTON_OK ) ) { return ButtonType.OK; } else if( type.equals( PROPERTY_BUTTON_CANCEL ) ) { return ButtonType.CANCEL; } else if( type.equals( PROPERTY_BUTTON_NEUTRAL ) ) { return ButtonType.NEUTRAL; } return null; } private void notifySelectionListener( final Listener listener ) { final Display display = Display.getCurrent(); ProcessActionRunner.add( new Runnable() { @Override public void run() { Event event = new Event(); event.display = display; listener.handleEvent( event ); } } ); } private void dispatchCloseEvent() { final List<ClientDialogListener> listeners = new ArrayList<ClientDialogListener>( dialogListeners ); ProcessActionRunner.add( new Runnable() { @Override public void run() { for( ClientDialogListener listener : listeners ) { listener.close(); } } } ); } RemoteObject getRemoteObject() { return remoteObject; } }