/******************************************************************************* * 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.rwt.lifecycle; import java.io.IOException; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import org.eclipse.rwt.internal.lifecycle.JSConst; import org.eclipse.rwt.internal.service.ContextProvider; import org.eclipse.rwt.internal.util.EncodingUtil; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.events.HelpEvent; import org.eclipse.swt.graphics.*; import org.eclipse.swt.internal.graphics.ResourceFactory; import org.eclipse.swt.internal.widgets.IWidgetGraphicsAdapter; import org.eclipse.swt.internal.widgets.Props; import org.eclipse.swt.widgets.*; /** * Utility class that provides a number of useful static methods to support the * implementation of widget life cycle adapters. * * @see ControlLCAUtil * @since 1.0 */ public final class WidgetLCAUtil { private static final String JS_PROP_HEIGHT = "height"; private static final String JS_PROP_WIDTH = "width"; private static final String PARAM_X = "bounds.x"; private static final String PARAM_Y = "bounds.y"; private static final String PARAM_WIDTH = "bounds.width"; private static final String PARAM_HEIGHT = "bounds.height"; private static final String PROP_TOOL_TIP_TEXT = "toolTip"; private static final String PROP_FONT = "font"; private static final String PROP_FOREGROUND = "foreground"; private static final String PROP_BACKGROUND = "background"; private static final String PROP_BACKGROUND_TRANSPARENCY = "backgroundTrans"; private static final String PROP_BACKGROUND_GRADIENT_COLORS = "backgroundGradientColors"; private static final String PROP_BACKGROUND_GRADIENT_PERCENTS = "backgroundGradientPercents"; private static final String PROP_ROUNDED_BORDER_WIDTH = "roundedBorderWidth"; private static final String PROP_ROUNDED_BORDER_COLOR = "roundedBorderColor"; private static final String PROP_ROUNDED_BORDER_RADIUS = "roundedBorderRadius"; private static final String PROP_ENABLED = "enabled"; private static final String PROP_VARIANT = "variant"; private static final String PROP_HELP_LISTENER = "helpListener"; private static final String JS_PROP_SPACE = "space"; private static final String JS_PROP_CONTEXT_MENU = "contextMenu"; private static final String JS_FUNC_SET_TOOL_TIP = "setToolTip"; private static final String JS_FUNC_SET_ROUNDED_BORDER = "setRoundedBorder"; private static final Pattern FONT_NAME_FILTER_PATTERN = Pattern.compile( "\"|\\\\" ); private static final JSListenerInfo HELP_LISTENER_INFO = new JSListenerInfo( "keydown", "org.eclipse.swt.EventUtil.helpRequested", JSListenerType.ACTION ); private static final Rectangle DEF_ROUNDED_BORDER_RADIUS = new Rectangle( 0, 0, 0, 0 ); ////////////////////////////////////////////////////////////////////////////// // TODO [fappel]: Experimental - profiler seems to indicate that buffering // improves performance - still under investigation. private final static Map parsedFonts = new HashMap(); ////////////////////////////////////////////////////////////////////////////// private WidgetLCAUtil() { // prevent instantiation } ///////////////////////////////////////////// // Methods to preserve common property values /** * Preserves the value of the property <code>bounds</code> of the * specified widget. * * @param widget the widget whose toolTip property to preserve * @param bounds the value to preserve * @see #writeBounds(Widget, Control, Rectangle) */ public static void preserveBounds( final Widget widget, final Rectangle bounds ) { IWidgetAdapter adapter = WidgetUtil.getAdapter( widget ); adapter.preserve( Props.BOUNDS, bounds ); } /** * Preserves the value of the property <code>toolTipText</code> of the * specified widget. * * @param widget the widget whose toolTip property to preserve * @param toolTip the value to preserve * @see #writeToolTip(Widget, String) */ public static void preserveToolTipText( final Widget widget, final String toolTip ) { String text = toolTip == null ? "" : toolTip; IWidgetAdapter adapter = WidgetUtil.getAdapter( widget ); adapter.preserve( PROP_TOOL_TIP_TEXT, text ); } /** * Preserves the value of the property <code>font</code> of the specified * widget. * * @param widget the widget whose font property to preserve * @param font the value to preserve * @see #writeFont(Widget, Font) */ public static void preserveFont( final Widget widget, final Font font ) { IWidgetAdapter adapter = WidgetUtil.getAdapter( widget ); adapter.preserve( PROP_FONT, font ); } /** * Preserves the value of the property <code>foreground</code> of the * specified widget. * * @param widget the widget whose foreground property to preserve * @param foreground the value to preserve * @see #writeForeground(Widget, Color) */ public static void preserveForeground( final Widget widget, final Color foreground ) { IWidgetAdapter adapter = WidgetUtil.getAdapter( widget ); adapter.preserve( PROP_FOREGROUND, foreground ); } /** * Preserves the value of the property <code>background</code> of the * specified widget. * * @param widget the widget whose background property to preserve * @param background the value to preserve * @see #writeBackground(Widget, Color) */ public static void preserveBackground( final Widget widget, final Color background ) { IWidgetAdapter adapter = WidgetUtil.getAdapter( widget ); adapter.preserve( PROP_BACKGROUND, background ); } /** * Preserves the value of the property <code>background</code> of the * specified widget. * * @param widget the widget whose background property to preserve * @param background the background color to preserve * @param transparency the background transparency to preserve * @see #writeBackground(Widget, Color, boolean) */ public static void preserveBackground( final Widget widget, final Color background, final boolean transparency ) { IWidgetAdapter adapter = WidgetUtil.getAdapter( widget ); adapter.preserve( PROP_BACKGROUND, background ); adapter.preserve( PROP_BACKGROUND_TRANSPARENCY, Boolean.valueOf( transparency ) ); } /** * Preserves the background gradient properties of the specified widget. * * @param widget the widget whose background gradient properties to preserve * @see #writeBackgroundGradient(Widget) * @since 1.3 */ public static void preserveBackgroundGradient( final Widget widget ) { Object adapter = widget.getAdapter( IWidgetGraphicsAdapter.class ); if( adapter != null ) { IWidgetGraphicsAdapter gfxAdapter = ( IWidgetGraphicsAdapter )adapter; Color[] bgGradientColors = gfxAdapter.getBackgroundGradientColors(); int[] bgGradientPercents = gfxAdapter.getBackgroundGradientPercents(); IWidgetAdapter widgetAdapter = WidgetUtil.getAdapter( widget ); widgetAdapter.preserve( PROP_BACKGROUND_GRADIENT_COLORS, bgGradientColors ); widgetAdapter.preserve( PROP_BACKGROUND_GRADIENT_PERCENTS, bgGradientPercents ); } } /** * Preserves the rounded border properties of the specified widget. * * @param widget the widget whose rounded border properties to preserve * @see #writeRoundedBorder(Widget) * @since 1.3 */ public static void preserveRoundedBorder( final Widget widget ) { Object adapter = widget.getAdapter( IWidgetGraphicsAdapter.class ); if( adapter != null ) { IWidgetGraphicsAdapter gfxAdapter = ( IWidgetGraphicsAdapter )adapter; int width = gfxAdapter.getRoundedBorderWidth(); Color color = gfxAdapter.getRoundedBorderColor(); Rectangle radius = gfxAdapter.getRoundedBorderRadius(); IWidgetAdapter widgetAdapter = WidgetUtil.getAdapter( widget ); widgetAdapter.preserve( PROP_ROUNDED_BORDER_WIDTH, new Integer( width ) ); widgetAdapter.preserve( PROP_ROUNDED_BORDER_COLOR, color ); widgetAdapter.preserve( PROP_ROUNDED_BORDER_RADIUS, radius ); } } /** * Preserves the value of the property <code>enabled</code> of the specified * widget. * * @param widget the widget whose enabled property to preserve * @param enabled the value to preserve * @see #writeEnabled(Widget, boolean) */ public static void preserveEnabled( final Widget widget, final boolean enabled ) { IWidgetAdapter adapter = WidgetUtil.getAdapter( widget ); adapter.preserve( PROP_ENABLED, Boolean.valueOf( enabled ) ); } /** * Preserves the value of the custom variant of the specified * widget. * * @param widget the widget whose custom variant to preserve * @see #writeCustomVariant(Widget) */ public static void preserveCustomVariant( final Widget widget ) { String variant = WidgetUtil.getVariant( widget ); IWidgetAdapter adapter = WidgetUtil.getAdapter( widget ); adapter.preserve( PROP_VARIANT, variant ); } //////////////////////////////////////////////////// // Methods to determine changes of widget properties /** * Determines whether the property of the given widget has changed during the * processing of the current request and thus the changes must be rendered in * the response. This is done by comparing the current value with the * preserved value. * <p> * If there is no preserved value, <code>null</code> is assumed. * </p> * * @param widget the widget whose property is to be compared, must not be * <code>null</code>. * @param property the name of the property under which the preserved value * can be looked up. Must not be <code>null</code>. * @param newValue the value to compare the preserved value with * @return <code>true</code> if the property has changed, <code>false</code> * otherwise */ public static boolean hasChanged( final Widget widget, final String property, final Object newValue ) { IWidgetAdapter adapter = WidgetUtil.getAdapter( widget ); Object oldValue = adapter.getPreserved( property ); return !WidgetLCAUtil.equals( oldValue, newValue ); } /** * Determines whether the property of the given widget has changed during the * processing of the current request and thus the changes must be rendered in * the response. This is done by comparing the current value with the * preserved value. * <p> * In case it is the first time that the widget is rendered (it is not yet * present on the client side) <code>true</code> is only returned if the * <code>newValue</code> differs from the <code>defaultValue</code>. * Otherwise the decision is delegated to * {@link #hasChanged(Widget,String,Object)}. * </p> * * @param widget the widget whose property is to be compared, must not be * <code>null</code>. * @param property the name of the property under which the preserved value * can be looked up. Must not be <code>null</code>. * @param newValue the value that is compared to the preserved value * @param defaultValue the default value * @return <code>true</code> if the property has changed or if the widget is * not yet initialized and the property is at its default value, * <code>false</code> otherwise */ public static boolean hasChanged( final Widget widget, final String property, final Object newValue, final Object defaultValue ) { boolean result; IWidgetAdapter adapter = WidgetUtil.getAdapter( widget ); if( adapter.isInitialized() ) { result = hasChanged( widget, property, newValue ); } else { result = !equals( newValue, defaultValue ); } return result; } /////////////////////////////////////////// // Methods to read request parameter values /** * Reads the value of the specified property for the specified widget from the * request that is currently processed. If this property is not submitted for * the given widget, <code>null</code> is returned. * * @param widget the widget whose property to read * @param propertyName the name of the property to read * @return the value read from the request or <code>null</code> if no value * was submitted for the given property */ // TODO: [fappel] create a clear specification how property names should look // like, in particular properties that are non primitive with // their own props. public static String readPropertyValue( final Widget widget, final String propertyName ) { HttpServletRequest request = ContextProvider.getRequest(); StringBuffer key = new StringBuffer(); key.append( WidgetUtil.getId( widget ) ); key.append( "." ); key.append( propertyName ); return request.getParameter( key.toString() ); } /** * Determines whether an event with the specified name was submitted for the * specified widget within the current request. * * @param widget the widget that should receive the event * @param eventName the name of the event to check for * @return <code>true</code> if the event was sent for the widget, false * otherwise. */ public static boolean wasEventSent( final Widget widget, final String eventName ) { HttpServletRequest request = ContextProvider.getRequest(); String widgetId = request.getParameter( eventName ); return WidgetUtil.getId( widget ).equals( widgetId ); } /** * Reads the bounds of the specified widget from the current request. If the * bounds of this widget was not sent with the current request, the specified * default is returned. * * @param widget the widget whose bounds to read * @param defValue the default bounds * @return the bounds as read from the request or the default bounds if no * bounds were passed within the current request */ public static Rectangle readBounds( final Widget widget, final Rectangle defValue ) { return readBounds( WidgetUtil.getId( widget ), defValue ); } /** * Reads the bounds of the widget specified by its id from the current * request. If the bounds of this widget was not sent with the current * request, the specified default is returned. * * @param widgetId the widget id of the widget whose bounds to read * @param defValue the default bounds * @return the bounds as read from the request or the default bounds if no * bounds were passed within the current request */ public static Rectangle readBounds( final String widgetId, final Rectangle defValue ) { int x = readBoundsX( widgetId, defValue.x ); int y = readBoundsY( widgetId, defValue.y ); int width = readBoundsWidth( widgetId, defValue.width ); int height = readBoundsHeight( widgetId, defValue.height ); return new Rectangle( x, y, width, height ); } ///////////////////////////////////////////////////////// // Methods to write JavaScript code for widget properties /** * Determines whether the bounds of the given widget have changed during the * processing of the current request and if so, writes JavaScript code to the * response that updates the client-side bounds of the specified widget. For * instances of {@link Control}, use the method * {@link ControlLCAUtil#writeBounds(Control)} instead. * * @param widget the widget whose bounds to write * @param parent the parent of the widget or <code>null</code> if the widget * does not have a parent * @param bounds the new bounds of the widget * @throws IOException */ public static void writeBounds( final Widget widget, final Control parent, final Rectangle bounds ) throws IOException { IWidgetAdapter adapter = WidgetUtil.getAdapter( widget ); // TODO [rh] replace code below with WidgetUtil.hasChanged Rectangle oldBounds = ( Rectangle )adapter.getPreserved( Props.BOUNDS ); Rectangle newBounds = bounds; if( !adapter.isInitialized() || !newBounds.equals( oldBounds ) ) { // the SWT coordinates for the client area differ in some cases from // the widget realization of qooxdoo if( parent != null ) { AbstractWidgetLCA parentLCA = WidgetUtil.getLCA( parent ); newBounds = parentLCA.adjustCoordinates( widget, newBounds ); } JSWriter writer = JSWriter.getWriterFor( widget ); // Note [rst] Children of ScrolledComposites must not render their x and y // coordinates as the content of SCs is scrolled automatically // by the client according to the position of the scroll bars. // Setting negative values breaks the layout on the client. if( parent instanceof ScrolledComposite ) { writer.set( JS_PROP_WIDTH, newBounds.width ); writer.set( JS_PROP_HEIGHT, newBounds.height ); } else { // [rh] for performance reasons, use the set(Object,Object[]) method Integer[] args = new Integer[] { new Integer( newBounds.x ), new Integer( newBounds.width ), new Integer( newBounds.y ), new Integer( newBounds.height ) }; writer.set( JS_PROP_SPACE, args ); } } } /** * Determines whether the property <code>menu</code> of the given widget has * changed during the processing of the current request and if so, writes * JavaScript code to the response that updates the client-side menu property * of the specified widget. For instances of {@link Control}, use the method * {@link ControlLCAUtil#writeMenu(Control)} instead. * * @param widget the widget whose menu property to set * @param menu the new value of the property * @throws IOException */ public static void writeMenu( final Widget widget, final Menu menu ) throws IOException { if( WidgetLCAUtil.hasChanged( widget, Props.MENU, menu, null ) ) { JSWriter writer = JSWriter.getWriterFor( widget ); writer.set( JS_PROP_CONTEXT_MENU, menu ); if( menu == null ) { writer.removeListener( JSConst.QX_EVENT_CONTEXTMENU, JSConst.JS_CONTEXT_MENU ); } else { writer.addListener( JSConst.QX_EVENT_CONTEXTMENU, JSConst.JS_CONTEXT_MENU ); } } } /** * Determines whether the property <code>toolTip</code> of the given widget * has changed during the processing of the current request and if so, writes * JavaScript code to the response that updates the client-side toolTip * property of the specified widget. For instances of {@link Control}, use * the method {@link ControlLCAUtil#writeToolTip(Control)} instead. * * @param widget the widget whose toolTip property to set * @param toolTip the new value of the property * @throws IOException * @see #preserveToolTipText(Widget, String) */ public static void writeToolTip( final Widget widget, final String toolTip ) throws IOException { String text = toolTip == null ? "" : toolTip; if( hasChanged( widget, WidgetLCAUtil.PROP_TOOL_TIP_TEXT, text, "" ) ) { JSWriter writer = JSWriter.getWriterFor( widget ); // Under Windows, ampersand characters are not correctly displayed: // https://bugs.eclipse.org/bugs/show_bug.cgi?id=188271 // However, it is correct not to escape mnemonics in tool tips text = escapeText( text, false ); text = replaceNewLines( text, "<br/>" ); Object[] args = new Object[] { widget, text }; writer.call( JSWriter.WIDGET_MANAGER_REF, JS_FUNC_SET_TOOL_TIP, args ); } } /** * Determines whether the property <code>image</code> of the given widget * has changed during the processing of the current request and if so, writes * JavaScript code to the response that updates the client-side image property * of the specified widget. * * @param widget the widget whose image property to set * @param image the new value of the property * @throws IOException */ public static void writeImage( final Widget widget, final Image image ) throws IOException { writeImage( widget, Props.IMAGE, JSConst.QX_FIELD_ICON, image ); } /** * Determines whether the specified image property of the given widget has * changed during the processing of the current request and if so, writes * JavaScript code to the response that updates the specified client-side * property of the specified widget. * * @param widget the widget whose property to set * @param javaProperty the key of the preserved value to compare the new value * with * @param jsProperty the name of the JavaScript property to set * @param image the new value of the property * @throws IOException */ public static void writeImage( final Widget widget, final String javaProperty, final String jsProperty, final Image image ) throws IOException { if( WidgetLCAUtil.hasChanged( widget, javaProperty, image, null ) ) { writeImage( widget, jsProperty, image ); } } /** * Writes JavaScript code to the response that sets the specified JavaScript * property of the specified widget to the specified image. * * @param widget the widget whose property to set * @param jsProperty the name of the JavaScript property to set * @param image the new value of the property * @throws IOException */ public static void writeImage( final Widget widget, final String jsProperty, final Image image ) throws IOException { String path = image == null ? null : ResourceFactory.getImagePath( image ); JSWriter writer = JSWriter.getWriterFor( widget ); writer.set( jsProperty, path ); } public static String[] parseFontName( final String name ) { synchronized( parsedFonts ) { String[] result = ( String[] )parsedFonts.get( name ); if( result == null ) { result = name.split( "," ); for( int i = 0; i < result.length; i++ ) { result[ i ] = result[ i ].trim(); Matcher matcher = FONT_NAME_FILTER_PATTERN.matcher( result[ i ] ); result[ i ] = matcher.replaceAll( "" ); } parsedFonts.put( name, result ); } return result; } } /** * Determines whether the property <code>font</code> of the given widget has * changed during the processing of the current request and if so, writes * JavaScript code to the response that updates the client-side font property * of the specified widget. For instances of {@link Control}, use the method * {@link ControlLCAUtil#writeFont(Control)} instead. * * @param widget the widget whose font property to set * @param font the new value of the property * @throws IOException * @see #preserveFont(Widget, Font) */ public static void writeFont( final Widget widget, final Font font ) throws IOException { if( WidgetLCAUtil.hasChanged( widget, PROP_FONT, font, null ) ) { JSWriter writer = JSWriter.getWriterFor( widget ); if( font != null ) { FontData fontData = font.getFontData()[ 0 ]; String[] names = parseFontName( fontData.getName() ); Object[] args = new Object[]{ widget, names, new Integer( fontData.getHeight() ), Boolean.valueOf( ( fontData.getStyle() & SWT.BOLD ) != 0 ), Boolean.valueOf( ( fontData.getStyle() & SWT.ITALIC ) != 0 ) }; writer.call( JSWriter.WIDGET_MANAGER_REF, "setFont", args ); } else { writer.reset( JSConst.QX_FIELD_FONT ); } } } /** * Determines whether the property <code>foreground</code> of the given * widget has changed during the processing of the current request and if so, * writes JavaScript code to the response that updates the client-side * foreground property of the specified widget. For instances of * {@link Control}, use the method * {@link ControlLCAUtil#writeForeground(Control)} instead. * * @param widget the widget whose foreground property to set * @param newColor the new value of the property * @throws IOException * @see #preserveForeground(Widget, Color) */ public static void writeForeground( final Widget widget, final Color newColor ) throws IOException { if( WidgetLCAUtil.hasChanged( widget, PROP_FOREGROUND, newColor, null ) ) { JSWriter writer = JSWriter.getWriterFor( widget ); if( newColor != null ) { writer.set( JSConst.QX_FIELD_COLOR, newColor ); } else { writer.reset( JSConst.QX_FIELD_COLOR ); } } } /** * Determines whether the property <code>background</code> of the given * widget has changed during the processing of the current request and if so, * writes JavaScript code to the response that updates the client-side * background property of the specified widget. For instances of * {@link Control}, use the method * {@link ControlLCAUtil#writeBackground(Control)} instead. * * @param widget the widget whose background property to set * @param newColor the new value of the property * @throws IOException * @see #preserveBackground(Widget, Color) */ public static void writeBackground( final Widget widget, final Color newColor ) throws IOException { writeBackground( widget, newColor, false ); } /** * Determines whether the property <code>background</code> of the given * widget has changed during the processing of the current request and if so, * writes JavaScript code to the response that updates the client-side * background property of the specified widget. For instances of * {@link Control}, use the method * {@link ControlLCAUtil#writeBackground(Control)} instead. * * @param widget the widget whose background property to set * @param background the new background color * @param transparency the new background transparency, if <code>true</code>, * the <code>background</code> parameter is ignored * @throws IOException * @see {@link #preserveBackground(Widget, Color, boolean)} */ public static void writeBackground( final Widget widget, final Color background, final boolean transparency ) throws IOException { JSWriter writer = JSWriter.getWriterFor( widget ); boolean changed = WidgetLCAUtil.hasChanged( widget, PROP_BACKGROUND_TRANSPARENCY, Boolean.valueOf( transparency ), Boolean.FALSE ); if( !changed && !transparency ) { changed = WidgetLCAUtil.hasChanged( widget, PROP_BACKGROUND, background, null ); } if( changed ) { if( transparency ) { writer.set( JSConst.QX_FIELD_BG_COLOR, ( Object )null ); } else if( background != null ) { writer.set( JSConst.QX_FIELD_BG_COLOR, background ); } else { writer.reset( JSConst.QX_FIELD_BG_COLOR ); } } } /** * Determines whether the background gradient properties of the * given widget have changed during the processing of the current request and * if so, writes JavaScript code to the response that updates the client-side * background gradient properties of the specified widget. * * @param widget the widget whose background gradient properties to set * @throws IOException * @see {@link #preserveBackgroundGradient(Widget)} * @since 1.3 */ public static void writeBackgroundGradient( final Widget widget ) throws IOException { if( hasBackgroundGradientChanged( widget ) ) { Object adapter = widget.getAdapter( IWidgetGraphicsAdapter.class ); IWidgetGraphicsAdapter graphicAdapter = ( IWidgetGraphicsAdapter )adapter; Color[] bgGradientColors = graphicAdapter.getBackgroundGradientColors(); int[] bgGradientPercents = graphicAdapter.getBackgroundGradientPercents(); JSWriter writer = JSWriter.getWriterFor( widget ); Integer[] percents = null; if( bgGradientPercents != null ) { percents = new Integer[ bgGradientPercents.length ]; for( int i = 0; i < bgGradientPercents.length; i++ ) { percents[ i ] = new Integer( bgGradientPercents[ i ] ); } } Object[] args = new Object[] { widget, bgGradientColors, percents }; writer.call( JSWriter.WIDGET_MANAGER_REF, "setBackgroundGradient", args ); } } private static boolean hasBackgroundGradientChanged( final Widget widget ) { Object adapter = widget.getAdapter( IWidgetGraphicsAdapter.class ); IWidgetGraphicsAdapter graphicsAdapter = ( IWidgetGraphicsAdapter )adapter; Color[] bgGradientColors = graphicsAdapter.getBackgroundGradientColors(); int[] bgGradientPercents = graphicsAdapter.getBackgroundGradientPercents(); return WidgetLCAUtil.hasChanged( widget, PROP_BACKGROUND_GRADIENT_COLORS, bgGradientColors, null ) || WidgetLCAUtil.hasChanged( widget, PROP_BACKGROUND_GRADIENT_PERCENTS, bgGradientPercents, null ); } /** * Determines whether the rounded border properties of the given widget has * changed during the processing of the current request and if so, writes * JavaScript code to the response that updates the client-side rounded border * of the specified widget. * * @param widget the widget whose rounded border properties to set * @throws IOException * @see {@link #preserveRoundedBorder(Widget)} * @since 1.3 */ public static void writeRoundedBorder( final Widget widget ) throws IOException { if( hasRoundedBorderChanged( widget ) ) { Object adapter = widget.getAdapter( IWidgetGraphicsAdapter.class ); IWidgetGraphicsAdapter graphicAdapter = ( IWidgetGraphicsAdapter )adapter; int width = graphicAdapter.getRoundedBorderWidth(); Rectangle radius = graphicAdapter.getRoundedBorderRadius(); Color color = graphicAdapter.getRoundedBorderColor(); Object[] args = new Object[] { widget, new Integer( width ), color, new Integer( radius.x ), new Integer( radius.y ), new Integer( radius.width ), new Integer( radius.height ) }; JSWriter writer = JSWriter.getWriterFor( widget ); writer.call( JSWriter.WIDGET_MANAGER_REF, JS_FUNC_SET_ROUNDED_BORDER, args ); } } private static boolean hasRoundedBorderChanged( final Widget widget ) { Object adapter = widget.getAdapter( IWidgetGraphicsAdapter.class ); IWidgetGraphicsAdapter graphicsAdapter = ( IWidgetGraphicsAdapter )adapter; int width = graphicsAdapter.getRoundedBorderWidth(); Color color = graphicsAdapter.getRoundedBorderColor(); Rectangle radius = graphicsAdapter.getRoundedBorderRadius(); return WidgetLCAUtil.hasChanged( widget, PROP_ROUNDED_BORDER_WIDTH, new Integer( width ), new Integer( 0 ) ) || WidgetLCAUtil.hasChanged( widget, PROP_ROUNDED_BORDER_COLOR, color, null ) || WidgetLCAUtil.hasChanged( widget, PROP_ROUNDED_BORDER_RADIUS, radius, DEF_ROUNDED_BORDER_RADIUS ); } /** * Determines whether the property <code>enabled</code> of the given widget * has changed during the processing of the current request and if so, writes * JavaScript code to the response that updates the client-side enabled * property of the specified widget. For instances of {@link Control}, use * the method {@link ControlLCAUtil#writeEnabled(Control)} instead. * * @param widget the widget whose enabled property to set * @param enabled the new value of the property * @throws IOException * @see #preserveEnabled(Widget, boolean) */ public static void writeEnabled( final Widget widget, final boolean enabled ) throws IOException { Boolean newValue = Boolean.valueOf( enabled ); JSWriter writer = JSWriter.getWriterFor( widget ); Boolean defValue = Boolean.TRUE; writer.set( Props.ENABLED, JSConst.QX_FIELD_ENABLED, newValue, defValue ); } /** * Replaces all newline characters in the specified input string with the * given replacement string. * * @param input the string to process * @param replacement the string to replace line feeds with * @return a new string with all line feeds replaced * @since 1.1 */ public static String replaceNewLines( final String input, final String replacement ) { return EncodingUtil.replaceNewLines( input, replacement ); } /** * Determines whether the custom variant of the given widget * has changed during the processing of the current request and if so, writes * JavaScript code to the response that updates the client-side variant. * * @param widget the widget whose custom variant to write * @throws IOException */ public static void writeCustomVariant( final Widget widget ) throws IOException { IWidgetAdapter adapter = WidgetUtil.getAdapter( widget ); String oldValue = ( String )adapter.getPreserved( PROP_VARIANT ); String newValue = WidgetUtil.getVariant( widget ); if( WidgetLCAUtil.hasChanged( widget, PROP_VARIANT, newValue, null ) ) { JSWriter writer = JSWriter.getWriterFor( widget ); Object[] args = new Object[] { "variant_" + oldValue }; if( oldValue != null ) { writer.call( JSConst.QX_FUNC_REMOVE_STATE, args ); } if( newValue != null ) { args = new Object[] { "variant_" + newValue }; writer.call( JSConst.QX_FUNC_ADD_STATE, args ); } } } /** * Checks whether a certain style flag is set on the specified widget and if * so, writes code to set the according state on the client-side widget. * * @param widget the widget whose style to write * @param style the SWT style flag in question * @param styleName the uppercase name of the style * @throws IOException * @since 1.2 */ public static void writeStyleFlag( final Widget widget, final int style, final String styleName ) throws IOException { JSWriter writer = JSWriter.getWriterFor( widget ); if( ( widget.getStyle() & style ) != 0 ) { writer.call( JSConst.QX_FUNC_ADD_STATE, new Object[] { "rwt_" + styleName } ); } } //////////////// // Help listener /** * Preserves whether the given <code>widget</code> has one or more * <code>HelpListener</code>s attached. * * @param widget the widget to preserve * @since 1.3 */ public static void preserveHelpListener( final Widget widget ) { IWidgetAdapter adapter = WidgetUtil.getAdapter( widget ); adapter.preserve( PROP_HELP_LISTENER, Boolean.valueOf( HelpEvent.hasListener( widget ) ) ); } /** * Adds or removes client-side help listeners for the the given * <code>widget</code> as necessary. * * @param widget * @since 1.3 */ public static void writeHelpListener( final Widget widget ) throws IOException { boolean hasListener = HelpEvent.hasListener( widget ); JSWriter writer = JSWriter.getWriterFor( widget ); writer.updateListener( HELP_LISTENER_INFO, PROP_HELP_LISTENER, hasListener ); } /** * Process a <code>HelpEvent</code> if the current request specifies that * there occured a help event for the given <code>widget</code>. * * @param widget the widget to process * @since 1.3 */ public static void processHelp( final Widget widget ) { if( WidgetLCAUtil.wasEventSent( widget, JSConst.EVENT_HELP ) ) { HelpEvent event = new HelpEvent( widget ); event.processEvent(); } } private static String readPropertyValue( final String widgetId, final String propertyName ) { HttpServletRequest request = ContextProvider.getRequest(); StringBuffer key = new StringBuffer(); key.append( widgetId ); key.append( "." ); key.append( propertyName ); return request.getParameter( key.toString() ); } ////////////////////////////////////////////////////////////////// // Helping methods to read bounds for a widget from request params private static int readBoundsY( final String widgetId, final int defValue ) { String value = readPropertyValue( widgetId, PARAM_Y ); return readBoundsValue( value, defValue ); } private static int readBoundsX( final String widgetId, final int defValue ) { String value = readPropertyValue( widgetId, PARAM_X ); return readBoundsValue( value, defValue ); } private static int readBoundsWidth( final String widgetId, final int defValue ) { String value = WidgetLCAUtil.readPropertyValue( widgetId, PARAM_WIDTH ); return readBoundsValue( value, defValue ); } private static int readBoundsHeight( final String widgetId, final int defValue ) { String value = WidgetLCAUtil.readPropertyValue( widgetId, PARAM_HEIGHT ); return readBoundsValue( value, defValue ); } private static int readBoundsValue( final String value, final int current ) { int result; if( value != null && !"null".equals( value ) ) { result = Integer.parseInt( value ); } else { result = current; } return result; } /////////////////////////////////////// // Helping method to test for equality static boolean equals( final Object object1, final Object object2 ) { boolean result; if( object1 == object2 ) { result = true; } else if( object1 == null ) { result = false; } else if( object1 instanceof boolean[] && object2 instanceof boolean[] ) { result = Arrays.equals( ( boolean[] )object1, ( boolean[] )object2 ); } else if( object1 instanceof int[] && object2 instanceof int[] ) { result = Arrays.equals( ( int[] )object1, ( int[] )object2 ); } else if( object1 instanceof long[] && object2 instanceof long[] ) { result = Arrays.equals( ( long[] )object1, ( long[] )object2 ); } else if( object1 instanceof float[] && object2 instanceof float[] ) { result = Arrays.equals( ( float[] )object1, ( float[] )object2 ); } else if( object1 instanceof double[] && object2 instanceof double[] ) { result = Arrays.equals( ( double[] )object1, ( double[] )object2 ); } else if( object1 instanceof Object[] && object2 instanceof Object[] ) { result = Arrays.equals( ( Object[] )object1, ( Object[] )object2 ); } else { result = object1.equals( object2 ); } return result; } ////////////////////////////////////// // Escaping of reserved XML characters /** * Replaces all occurrences of the characters <code><</code>, * <code>></code>, <code>&</code>, and <code>"</code> with * their corresponding HTML entities. This function is used for rendering * texts to the client. When the parameter mnemonic is set to * <code>true</code>, this method handles ampersand characters in the text * as mnemonics in the same manner as SWT does. * <p> * <strong>Note:</strong> In contrast to SWT, the characters following an * ampersand are currently not underlined, as RAP doesn't support key events * yet. * </p> * * @param text the input text * @param mnemonics if <code>true</code>, the function is mnemonic aware, * otherwise all ampersand characters are directly rendered. * @return the resulting text */ // Note [rst]: Single quotes are not escaped as the entity ' is not // defined in HTML 4. They should be handled by this method once // we produce XHTML output. public static String escapeText( final String text, final boolean mnemonics ) { boolean insertAmp = false; StringBuffer buffer = new StringBuffer(); int textLength = text.length(); for( int i = 0; i < textLength; i++ ) { char ch = text.charAt( i ); if( ch == '&' ) { if( !mnemonics || insertAmp ) { insertAmp = false; buffer.append( "&" ); } else { if( i + 1 < textLength && text.charAt( i + 1 ) == '&' ) { insertAmp = true; } } } else if( ch == '<' ) { buffer.append( "<" ); } else if( ch == '>' ) { buffer.append( ">" ); } else if( ch == '"' ) { buffer.append( """ ); } else if( EncodingUtil.isNonDisplayableChar( ch ) ) { // Escape \u2028 and \u2029 - see bug 304364 buffer.append( "&#" ); buffer.append( ( int )ch ); buffer.append( ";" ); } else { buffer.append( ch ); } } // truncate at zeros String result = buffer.toString(); int index = result.indexOf( 0 ); if( index != -1 ) { result = result.substring( 0, index ); } return result; } /** * Writes JavaScript code to the response that resets the bounds of a widget. * This method is intended to be used by implementations of the method * {@link AbstractWidgetLCA#createResetHandlerCalls(String)}. * * @throws IOException * @deprecated As of 1.3, server-side widget pooling is no longer required. * This method does nothing. */ public static void resetBounds() throws IOException { } /** * Writes JavaScript code to the response that resets the property * <code>menu</code> of a widget. This method is intended to be used by * implementations of the method * {@link AbstractWidgetLCA#createResetHandlerCalls(String)}. * * @throws IOException * @deprecated As of 1.3, server-side widget pooling is no longer required. * This method does nothing. */ public static void resetMenu() throws IOException { } /** * Writes JavaScript code to the response that resets the property * <code>toolTip</code> of a widget. This method is intended to be used by * implementations of the method * {@link AbstractWidgetLCA#createResetHandlerCalls(String)}. * * @throws IOException * @deprecated As of 1.3, server-side widget pooling is no longer required. * This method does nothing. */ public static void resetToolTip() throws IOException { } /** * Writes JavaScript code to the response that resets the property * <code>font</code> of a widget. This method is intended to be used by * implementations of the method * {@link AbstractWidgetLCA#createResetHandlerCalls(String)}. * * @throws IOException * @deprecated As of 1.3, server-side widget pooling is no longer required. * This method does nothing. */ public static void resetFont() throws IOException { } /** * Writes JavaScript code to the response that resets the property * <code>foreground</code> of a widget. This method is intended to be used * by implementations of the method * {@link AbstractWidgetLCA#createResetHandlerCalls(String)}. * * @throws IOException * @deprecated As of 1.3, server-side widget pooling is no longer required. * This method does nothing. */ public static void resetForeground() throws IOException { } /** * Writes JavaScript code to the response that reset the property * <code>background</code> of a widget. This method is intended to be used * by implementations of the method * {@link AbstractWidgetLCA#createResetHandlerCalls(String)}. * * @throws IOException * @deprecated As of 1.3, server-side widget pooling is no longer required. * This method does nothing. */ public static void resetBackground() throws IOException { } /** * Writes JavaScript code to the response that resets the property * <code>enabled</code> of a widget. This method is intended to be used by * implementations of the method * {@link AbstractWidgetLCA#createResetHandlerCalls(String)}. * * @throws IOException * @deprecated As of 1.3, server-side widget pooling is no longer required. * This method does nothing. */ public static void resetEnabled() throws IOException { } }