From: Henri Sara Date: Mon, 11 May 2009 13:53:36 +0000 (+0000) Subject: #2904 Change client side class name prefixes I -> V (class names only, not yet CSS... X-Git-Tag: 6.7.0.beta1~2922 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=1981ddc3b1a44de32e145c03a89b5a0f27807ac4;p=vaadin-framework.git #2904 Change client side class name prefixes I -> V (class names only, not yet CSS prefixes) svn changeset:7742/svn branch:6.0 --- diff --git a/src/com/vaadin/demo/colorpicker/gwt/client/ColorPickerWidgetSet.java b/src/com/vaadin/demo/colorpicker/gwt/client/ColorPickerWidgetSet.java index 0a73b6eb5a..ed6b32b6cd 100644 --- a/src/com/vaadin/demo/colorpicker/gwt/client/ColorPickerWidgetSet.java +++ b/src/com/vaadin/demo/colorpicker/gwt/client/ColorPickerWidgetSet.java @@ -4,7 +4,7 @@ package com.vaadin.demo.colorpicker.gwt.client; -import com.vaadin.demo.colorpicker.gwt.client.ui.IColorPicker; +import com.vaadin.demo.colorpicker.gwt.client.ui.VColorPicker; import com.vaadin.terminal.gwt.client.DefaultWidgetSet; import com.vaadin.terminal.gwt.client.Paintable; import com.vaadin.terminal.gwt.client.UIDL; @@ -15,7 +15,7 @@ public class ColorPickerWidgetSet extends DefaultWidgetSet { protected Class resolveWidgetType(UIDL uidl) { final String tag = uidl.getTag(); if ("colorpicker".equals(tag)) { - return IColorPicker.class; + return VColorPicker.class; } // Let the DefaultWidgetSet handle resolution of default widgets @@ -26,8 +26,8 @@ public class ColorPickerWidgetSet extends DefaultWidgetSet { @Override public Paintable createWidget(UIDL uidl) { final Class type = resolveWidgetType(uidl); - if (IColorPicker.class == type) { - return new IColorPicker(); + if (VColorPicker.class == type) { + return new VColorPicker(); } // Let the DefaultWidgetSet handle creation of default widgets diff --git a/src/com/vaadin/demo/colorpicker/gwt/client/ui/IColorPicker.java b/src/com/vaadin/demo/colorpicker/gwt/client/ui/IColorPicker.java deleted file mode 100644 index f238e9dc3f..0000000000 --- a/src/com/vaadin/demo/colorpicker/gwt/client/ui/IColorPicker.java +++ /dev/null @@ -1,82 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.demo.colorpicker.gwt.client.ui; - -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; - -public class IColorPicker extends GwtColorPicker implements Paintable { - - /** Set the CSS class name to allow styling. */ - public static final String CLASSNAME = "example-colorpicker"; - - /** Component identifier in UIDL communications. */ - String uidlId; - - /** Reference to the server connection object. */ - ApplicationConnection client; - - /** - * The constructor should first call super() to initialize the component and - * then handle any initialization relevant to IT Mill Toolkit. - */ - public IColorPicker() { - // The superclass has a lot of relevant initialization - super(); - - // This method call of the Paintable interface sets the component - // style name in DOM tree - setStyleName(CLASSNAME); - } - - /** - * This method must be implemented to update the client-side component from - * UIDL data received from server. - * - * This method is called when the page is loaded for the first time, and - * every time UI changes in the component are received from the server. - */ - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - // This call should be made first. Ensure correct implementation, - // and let the containing layout manage caption, etc. - if (client.updateComponent(this, uidl, true)) { - return; - } - - // Save reference to server connection object to be able to send - // user interaction later - this.client = client; - - // Save the UIDL identifier for the component - uidlId = uidl.getId(); - - // Get value received from server and actualize it in the GWT component - setColor(uidl.getStringVariable("colorname")); - } - - /** Override the method to communicate the new value to server. */ - @Override - public void setColor(String newcolor) { - // Ignore if no change - if (newcolor.equals(currentcolor.getText())) { - return; - } - - // Let the original implementation to do whatever it needs to do - super.setColor(newcolor); - - // Updating the state to the server can not be done before - // the server connection is known, i.e., before updateFromUIDL() - // has been called. - if (uidlId == null || client == null) { - return; - } - - // Communicate the user interaction parameters to server. This call will - // initiate an AJAX request to the server. - client.updateVariable(uidlId, "colorname", newcolor, true); - } -} diff --git a/src/com/vaadin/demo/colorpicker/gwt/client/ui/VColorPicker.java b/src/com/vaadin/demo/colorpicker/gwt/client/ui/VColorPicker.java new file mode 100644 index 0000000000..7b90653c6a --- /dev/null +++ b/src/com/vaadin/demo/colorpicker/gwt/client/ui/VColorPicker.java @@ -0,0 +1,82 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.demo.colorpicker.gwt.client.ui; + +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; + +public class VColorPicker extends GwtColorPicker implements Paintable { + + /** Set the CSS class name to allow styling. */ + public static final String CLASSNAME = "example-colorpicker"; + + /** Component identifier in UIDL communications. */ + String uidlId; + + /** Reference to the server connection object. */ + ApplicationConnection client; + + /** + * The constructor should first call super() to initialize the component and + * then handle any initialization relevant to IT Mill Toolkit. + */ + public VColorPicker() { + // The superclass has a lot of relevant initialization + super(); + + // This method call of the Paintable interface sets the component + // style name in DOM tree + setStyleName(CLASSNAME); + } + + /** + * This method must be implemented to update the client-side component from + * UIDL data received from server. + * + * This method is called when the page is loaded for the first time, and + * every time UI changes in the component are received from the server. + */ + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + // This call should be made first. Ensure correct implementation, + // and let the containing layout manage caption, etc. + if (client.updateComponent(this, uidl, true)) { + return; + } + + // Save reference to server connection object to be able to send + // user interaction later + this.client = client; + + // Save the UIDL identifier for the component + uidlId = uidl.getId(); + + // Get value received from server and actualize it in the GWT component + setColor(uidl.getStringVariable("colorname")); + } + + /** Override the method to communicate the new value to server. */ + @Override + public void setColor(String newcolor) { + // Ignore if no change + if (newcolor.equals(currentcolor.getText())) { + return; + } + + // Let the original implementation to do whatever it needs to do + super.setColor(newcolor); + + // Updating the state to the server can not be done before + // the server connection is known, i.e., before updateFromUIDL() + // has been called. + if (uidlId == null || client == null) { + return; + } + + // Communicate the user interaction parameters to server. This call will + // initiate an AJAX request to the server. + client.updateVariable(uidlId, "colorname", newcolor, true); + } +} diff --git a/src/com/vaadin/demo/coverflow/gwt/client/CoverflowWidgetSet.java b/src/com/vaadin/demo/coverflow/gwt/client/CoverflowWidgetSet.java index 38037a4ba0..e9fc76dae8 100644 --- a/src/com/vaadin/demo/coverflow/gwt/client/CoverflowWidgetSet.java +++ b/src/com/vaadin/demo/coverflow/gwt/client/CoverflowWidgetSet.java @@ -4,7 +4,7 @@ package com.vaadin.demo.coverflow.gwt.client; -import com.vaadin.demo.coverflow.gwt.client.ui.ICoverflow; +import com.vaadin.demo.coverflow.gwt.client.ui.VCoverflow; import com.vaadin.terminal.gwt.client.DefaultWidgetSet; import com.vaadin.terminal.gwt.client.Paintable; import com.vaadin.terminal.gwt.client.UIDL; @@ -13,8 +13,8 @@ public class CoverflowWidgetSet extends DefaultWidgetSet { /** Creates a widget according to its class name. */ public Paintable createWidget(UIDL uidl) { final Class classType = resolveWidgetType(uidl); - if (ICoverflow.class == classType) { - return new ICoverflow(); + if (VCoverflow.class == classType) { + return new VCoverflow(); } // Let the DefaultWidgetSet handle creation of default widgets @@ -25,7 +25,7 @@ public class CoverflowWidgetSet extends DefaultWidgetSet { protected Class resolveWidgetType(UIDL uidl) { final String tag = uidl.getTag(); if ("cover".equals(tag)) { - return ICoverflow.class; + return VCoverflow.class; } // Let the DefaultWidgetSet handle resolution of default widgets diff --git a/src/com/vaadin/demo/coverflow/gwt/client/ui/ICoverflow.java b/src/com/vaadin/demo/coverflow/gwt/client/ui/ICoverflow.java deleted file mode 100644 index c09d18c7d8..0000000000 --- a/src/com/vaadin/demo/coverflow/gwt/client/ui/ICoverflow.java +++ /dev/null @@ -1,336 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.demo.coverflow.gwt.client.ui; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.HTML; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; - -public class ICoverflow extends Composite implements Paintable { - private String uidlId; - protected ApplicationConnection client; - private ArrayList coverList = new ArrayList(); - - private Object _selected; - private boolean flashInited = false; - private HTML flash; - private boolean scrollbarVisibility = true; - private String backgroundGradientStart; - private String backgroundGradientEnd; - private boolean colorChanged = false; - private boolean sbVisibilityChanged = false; - private HashMap keyMap = new HashMap(); - - /** - * Constructor - */ - public ICoverflow() { - flash = new HTML(); - - initWidget(flash); - } - - /** - * This method accepts parses the uidl sent by the server - * - * @param UIDL - * uidl - * @param ApplicationConnection - * client - */ - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - // Store variables - uidlId = uidl.getId(); - this.client = client; - String tempColor; - - if (client.updateComponent(this, uidl, true)) { - return; - } - - // Has the scrollbar's visibility status changed? - if (uidl.hasAttribute("scrollbarVisibility")) { - boolean tempVis = uidl.getBooleanAttribute("scrollbarVisibility"); - if (scrollbarVisibility != tempVis) { - scrollbarVisibility = tempVis; - sbVisibilityChanged = true; - } - } - - // Has the start gradient changed? - if (uidl.hasAttribute("backgroundGradientStart")) { - tempColor = uidl.getStringAttribute("backgroundGradientStart") - .toString(); - if (tempColor != backgroundGradientStart) { - backgroundGradientStart = tempColor; - colorChanged = true; - } - } - - // Has the end gradient changed? - if (uidl.hasAttribute("backgroundGradientEnd")) { - tempColor = uidl.getStringAttribute("backgroundGradientEnd") - .toString(); - if (tempColor != backgroundGradientEnd) { - backgroundGradientEnd = tempColor; - colorChanged = true; - } - } - - final UIDL images = uidl.getChildUIDL(0); - - // Check which covers should be removed. This array list contains all - // current - // covers. We remove from this list all covers which are sent with the - // repainted - // uidl. All remaining covers in this list should be "old" ones and are - // should - // be deleted. - - ArrayList newList = new ArrayList(); - - // Iterate through all option elements - for (final Iterator i = images.getChildIterator(); i.hasNext();) { - final UIDL imgUidl = (UIDL) i.next(); - - // Make sure all required attributes exist - if (imgUidl.hasAttribute("caption") && imgUidl.hasAttribute("key") - && imgUidl.hasAttribute("icon")) { - HashMap set = new HashMap(); - - // Update the key map - keyMap.put(imgUidl.getStringAttribute("caption"), imgUidl - .getStringAttribute("key")); - - // Get information - - set.put("icon", client.translateToolkitUri(imgUidl - .getStringAttribute("icon"))); - set.put("caption", imgUidl.getStringAttribute("caption")); - - newList.add(set); - - // Is the current cover selected? - if (imgUidl.hasAttribute("selected")) { - _selected = imgUidl.getStringAttribute("caption"); - } - } - } - - // Deleted items - ArrayList intersectList = new ArrayList(); - intersectList.addAll(coverList); - intersectList.removeAll(newList); - - if (flashInited) { - for (int i = 0; i < intersectList.size(); i++) { - HashMap cover = (HashMap) intersectList.get(i); - removeCover(uidlId, cover.get("caption").toString()); - } - } - - // Added items - intersectList = new ArrayList(); - intersectList.addAll(newList); - intersectList.removeAll(coverList); - - if (flashInited) { - for (int i = 0; i < intersectList.size(); i++) { - HashMap cover = (HashMap) intersectList.get(i); - addCover(uidlId, cover.get("caption").toString(), cover.get( - "icon").toString()); - } - } - - coverList = newList; - - // Has the flash been initialized? - if (!flashInited) { - colorChanged = false; - setFlash(); - initializeMethods(uidlId); - } - - // Inform flash of the selected cover - if (_selected != null && flashInited) { - selectCover(uidlId, _selected.toString()); - } - - if (colorChanged && flashInited) { - setBackgroundColor(uidlId, backgroundGradientStart, - backgroundGradientEnd); - colorChanged = false; - } - - if (sbVisibilityChanged && flashInited) { - toggleScrollbarVisibility(uidlId, scrollbarVisibility); - sbVisibilityChanged = false; - } - - } - - /** - * Inform the server which cover is selected - * - * @param String - * coverKey - */ - public void setCover(String coverId) { - if (uidlId == null || client == null) { - return; - } - - client.updateVariable(uidlId, "selected", new String[] { keyMap.get( - coverId).toString() }, true); - } - - /** - * Initialize the native javascript functions needed for the flash <-> GWT - * communication - * - * @param String - * id - */ - public native void initializeMethods(String id) /*-{ - var app = this; - - if($wnd.itmill.coverflow == null) - var coverflow = []; - else - var coverflow = $wnd.itmill.coverflow; - - coverflow['getCovers_' + id] = function() { - app.@com.vaadin.demo.coverflow.gwt.client.ui.ICoverflow::getCovers()(); - }; - - coverflow['setCurrent_' + id] = function(selected) { - app.@com.vaadin.demo.coverflow.gwt.client.ui.ICoverflow::setCover(Ljava/lang/String;)(selected); - }; - - $wnd.itmill.coverflow = coverflow; - }-*/; - - /** - * This function sends all covers to the flash. We cannot do this directly - * in the updateFromUIDL method, because we cannot be sure if the flash has - * been loaded into the browser. The flash will call for this method when - * it's ready. - */ - public void getCovers() { - // Loop through all stored coves - for (int i = 0; i < coverList.size(); i++) { - HashMap set = (HashMap) coverList.get(i); - - try { - // Add the cover - addCover(uidlId, set.get("caption").toString(), set.get("icon") - .toString()); - } catch (Exception e) { - // Do not add covers lacking obligatory data - } - } - // The flash calls for this method, therefore we can be sure that the - // flash has been loaded - // into the browser. - flashInited = true; - - // Set selected cover - if (_selected != null) { - selectCover(uidlId, _selected.toString()); - } - } - - /** - * This function is a native javascript function which adds covers to the - * actual flash. This method works as a bridge between GWT and flash. - * - * @param id - * @param key - * @param caption - * @param icon - */ - public native void addCover(String id, String caption, String icon) /*-{ - try { - $doc['fxcoverflow' + id].addCover(caption.toString(), icon.toString()); - } - catch(e) { - $wnd.alert(e.message); - } - - }-*/; - - /** - * This function tells the flash which cover should be selected. - * - * @param id - * @param key - */ - public native void selectCover(String id, String key) /*-{ - $doc["fxcoverflow" + id].selectCover(key.toString()); - }-*/; - - public native void setBackgroundColor(String id, String startGradient, - String endGradient) /*-{ - $doc["fxcoverflow" + id].setBackgroundColor("0x" + startGradient.toString(), "0x" + endGradient.toString()); - }-*/; - - public native void toggleScrollbarVisibility(String id, boolean visibility) /*-{ - $doc["fxcoverflow" + id].toggleScrollbarVisibility(visibility); - }-*/; - - public native void removeCover(String id, String key) /*-{ - $doc["fxcoverflow" + id].removeCover(key); - }-*/; - - /** - * Set the HTML coding of the flash movie. This isn't done until the - * updateFromUIDL method is called for the first time. The reason is that we - * use an id from the UIDL to uniquely identify all instances of this - * widget. - */ - private void setFlash() { - String html = "" - + "" - + "" - + "" - + "" - + "" + ""; - flash.setHTML(html); - } -} \ No newline at end of file diff --git a/src/com/vaadin/demo/coverflow/gwt/client/ui/VCoverflow.java b/src/com/vaadin/demo/coverflow/gwt/client/ui/VCoverflow.java new file mode 100644 index 0000000000..a09c9b1865 --- /dev/null +++ b/src/com/vaadin/demo/coverflow/gwt/client/ui/VCoverflow.java @@ -0,0 +1,336 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.demo.coverflow.gwt.client.ui; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.HTML; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; + +public class VCoverflow extends Composite implements Paintable { + private String uidlId; + protected ApplicationConnection client; + private ArrayList coverList = new ArrayList(); + + private Object _selected; + private boolean flashInited = false; + private HTML flash; + private boolean scrollbarVisibility = true; + private String backgroundGradientStart; + private String backgroundGradientEnd; + private boolean colorChanged = false; + private boolean sbVisibilityChanged = false; + private HashMap keyMap = new HashMap(); + + /** + * Constructor + */ + public VCoverflow() { + flash = new HTML(); + + initWidget(flash); + } + + /** + * This method accepts parses the uidl sent by the server + * + * @param UIDL + * uidl + * @param ApplicationConnection + * client + */ + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + // Store variables + uidlId = uidl.getId(); + this.client = client; + String tempColor; + + if (client.updateComponent(this, uidl, true)) { + return; + } + + // Has the scrollbar's visibility status changed? + if (uidl.hasAttribute("scrollbarVisibility")) { + boolean tempVis = uidl.getBooleanAttribute("scrollbarVisibility"); + if (scrollbarVisibility != tempVis) { + scrollbarVisibility = tempVis; + sbVisibilityChanged = true; + } + } + + // Has the start gradient changed? + if (uidl.hasAttribute("backgroundGradientStart")) { + tempColor = uidl.getStringAttribute("backgroundGradientStart") + .toString(); + if (tempColor != backgroundGradientStart) { + backgroundGradientStart = tempColor; + colorChanged = true; + } + } + + // Has the end gradient changed? + if (uidl.hasAttribute("backgroundGradientEnd")) { + tempColor = uidl.getStringAttribute("backgroundGradientEnd") + .toString(); + if (tempColor != backgroundGradientEnd) { + backgroundGradientEnd = tempColor; + colorChanged = true; + } + } + + final UIDL images = uidl.getChildUIDL(0); + + // Check which covers should be removed. This array list contains all + // current + // covers. We remove from this list all covers which are sent with the + // repainted + // uidl. All remaining covers in this list should be "old" ones and are + // should + // be deleted. + + ArrayList newList = new ArrayList(); + + // Iterate through all option elements + for (final Iterator i = images.getChildIterator(); i.hasNext();) { + final UIDL imgUidl = (UIDL) i.next(); + + // Make sure all required attributes exist + if (imgUidl.hasAttribute("caption") && imgUidl.hasAttribute("key") + && imgUidl.hasAttribute("icon")) { + HashMap set = new HashMap(); + + // Update the key map + keyMap.put(imgUidl.getStringAttribute("caption"), imgUidl + .getStringAttribute("key")); + + // Get information + + set.put("icon", client.translateToolkitUri(imgUidl + .getStringAttribute("icon"))); + set.put("caption", imgUidl.getStringAttribute("caption")); + + newList.add(set); + + // Is the current cover selected? + if (imgUidl.hasAttribute("selected")) { + _selected = imgUidl.getStringAttribute("caption"); + } + } + } + + // Deleted items + ArrayList intersectList = new ArrayList(); + intersectList.addAll(coverList); + intersectList.removeAll(newList); + + if (flashInited) { + for (int i = 0; i < intersectList.size(); i++) { + HashMap cover = (HashMap) intersectList.get(i); + removeCover(uidlId, cover.get("caption").toString()); + } + } + + // Added items + intersectList = new ArrayList(); + intersectList.addAll(newList); + intersectList.removeAll(coverList); + + if (flashInited) { + for (int i = 0; i < intersectList.size(); i++) { + HashMap cover = (HashMap) intersectList.get(i); + addCover(uidlId, cover.get("caption").toString(), cover.get( + "icon").toString()); + } + } + + coverList = newList; + + // Has the flash been initialized? + if (!flashInited) { + colorChanged = false; + setFlash(); + initializeMethods(uidlId); + } + + // Inform flash of the selected cover + if (_selected != null && flashInited) { + selectCover(uidlId, _selected.toString()); + } + + if (colorChanged && flashInited) { + setBackgroundColor(uidlId, backgroundGradientStart, + backgroundGradientEnd); + colorChanged = false; + } + + if (sbVisibilityChanged && flashInited) { + toggleScrollbarVisibility(uidlId, scrollbarVisibility); + sbVisibilityChanged = false; + } + + } + + /** + * Inform the server which cover is selected + * + * @param String + * coverKey + */ + public void setCover(String coverId) { + if (uidlId == null || client == null) { + return; + } + + client.updateVariable(uidlId, "selected", new String[] { keyMap.get( + coverId).toString() }, true); + } + + /** + * Initialize the native javascript functions needed for the flash <-> GWT + * communication + * + * @param String + * id + */ + public native void initializeMethods(String id) /*-{ + var app = this; + + if($wnd.itmill.coverflow == null) + var coverflow = []; + else + var coverflow = $wnd.itmill.coverflow; + + coverflow['getCovers_' + id] = function() { + app.@com.vaadin.demo.coverflow.gwt.client.ui.VCoverflow::getCovers()(); + }; + + coverflow['setCurrent_' + id] = function(selected) { + app.@com.vaadin.demo.coverflow.gwt.client.ui.VCoverflow::setCover(Ljava/lang/String;)(selected); + }; + + $wnd.itmill.coverflow = coverflow; + }-*/; + + /** + * This function sends all covers to the flash. We cannot do this directly + * in the updateFromUIDL method, because we cannot be sure if the flash has + * been loaded into the browser. The flash will call for this method when + * it's ready. + */ + public void getCovers() { + // Loop through all stored coves + for (int i = 0; i < coverList.size(); i++) { + HashMap set = (HashMap) coverList.get(i); + + try { + // Add the cover + addCover(uidlId, set.get("caption").toString(), set.get("icon") + .toString()); + } catch (Exception e) { + // Do not add covers lacking obligatory data + } + } + // The flash calls for this method, therefore we can be sure that the + // flash has been loaded + // into the browser. + flashInited = true; + + // Set selected cover + if (_selected != null) { + selectCover(uidlId, _selected.toString()); + } + } + + /** + * This function is a native javascript function which adds covers to the + * actual flash. This method works as a bridge between GWT and flash. + * + * @param id + * @param key + * @param caption + * @param icon + */ + public native void addCover(String id, String caption, String icon) /*-{ + try { + $doc['fxcoverflow' + id].addCover(caption.toString(), icon.toString()); + } + catch(e) { + $wnd.alert(e.message); + } + + }-*/; + + /** + * This function tells the flash which cover should be selected. + * + * @param id + * @param key + */ + public native void selectCover(String id, String key) /*-{ + $doc["fxcoverflow" + id].selectCover(key.toString()); + }-*/; + + public native void setBackgroundColor(String id, String startGradient, + String endGradient) /*-{ + $doc["fxcoverflow" + id].setBackgroundColor("0x" + startGradient.toString(), "0x" + endGradient.toString()); + }-*/; + + public native void toggleScrollbarVisibility(String id, boolean visibility) /*-{ + $doc["fxcoverflow" + id].toggleScrollbarVisibility(visibility); + }-*/; + + public native void removeCover(String id, String key) /*-{ + $doc["fxcoverflow" + id].removeCover(key); + }-*/; + + /** + * Set the HTML coding of the flash movie. This isn't done until the + * updateFromUIDL method is called for the first time. The reason is that we + * use an id from the UIDL to uniquely identify all instances of this + * widget. + */ + private void setFlash() { + String html = "" + + "" + + "" + + "" + + "" + + "" + ""; + flash.setHTML(html); + } +} \ No newline at end of file diff --git a/src/com/vaadin/demo/reservation/gwt/client/ReservationWidgetSet.java b/src/com/vaadin/demo/reservation/gwt/client/ReservationWidgetSet.java index 9bf5f22061..01577a6917 100644 --- a/src/com/vaadin/demo/reservation/gwt/client/ReservationWidgetSet.java +++ b/src/com/vaadin/demo/reservation/gwt/client/ReservationWidgetSet.java @@ -4,8 +4,8 @@ package com.vaadin.demo.reservation.gwt.client; -import com.vaadin.demo.reservation.gwt.client.ui.ICalendarField; -import com.vaadin.demo.reservation.gwt.client.ui.IGoogleMap; +import com.vaadin.demo.reservation.gwt.client.ui.VCalendarField; +import com.vaadin.demo.reservation.gwt.client.ui.VGoogleMap; import com.vaadin.terminal.gwt.client.DefaultWidgetSet; import com.vaadin.terminal.gwt.client.Paintable; import com.vaadin.terminal.gwt.client.UIDL; @@ -14,10 +14,10 @@ public class ReservationWidgetSet extends DefaultWidgetSet { @Override public Paintable createWidget(UIDL uidl) { final Class type = resolveWidgetType(uidl); - if (IGoogleMap.class == type) { - return new IGoogleMap(); - } else if (ICalendarField.class == type) { - return new ICalendarField(); + if (VGoogleMap.class == type) { + return new VGoogleMap(); + } else if (VCalendarField.class == type) { + return new VCalendarField(); } return super.createWidget(uidl); @@ -27,9 +27,9 @@ public class ReservationWidgetSet extends DefaultWidgetSet { protected Class resolveWidgetType(UIDL uidl) { final String tag = uidl.getTag(); if ("googlemap".equals(tag)) { - return IGoogleMap.class; + return VGoogleMap.class; } else if ("calendarfield".equals(tag)) { - return ICalendarField.class; + return VCalendarField.class; } return super.resolveWidgetType(uidl); } diff --git a/src/com/vaadin/demo/reservation/gwt/client/ui/ICalendarField.java b/src/com/vaadin/demo/reservation/gwt/client/ui/ICalendarField.java deleted file mode 100644 index a11321dfc0..0000000000 --- a/src/com/vaadin/demo/reservation/gwt/client/ui/ICalendarField.java +++ /dev/null @@ -1,294 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.demo.reservation.gwt.client.ui; - -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; - -import com.google.gwt.i18n.client.DateTimeFormat; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.ui.FlexTable; -import com.google.gwt.user.client.ui.SimplePanel; -import com.google.gwt.user.client.ui.SourcesTableEvents; -import com.google.gwt.user.client.ui.TableListener; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.DateTimeService; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.ui.CalendarEntry; -import com.vaadin.terminal.gwt.client.ui.ICalendarPanel; -import com.vaadin.terminal.gwt.client.ui.IDateField; - -public class ICalendarField extends IDateField { - - private final ICalendarPanel calPanel; - - private SimplePanel hourPanel; - - private FlexTable hourTable; - - private final EntrySource entrySource; - - private final TableListener ftListener = new HourTableListener(); - - private int realResolution = RESOLUTION_DAY; - - private static final String CLASSNAME = IDateField.CLASSNAME - + "-entrycalendar"; - - public ICalendarField() { - super(); - setStyleName(CLASSNAME); - calPanel = new ICalendarPanel(this); - add(calPanel); - entrySource = new EntrySource(); - calPanel.setCalendarEntrySource(entrySource); - calPanel.addTableListener(new TableListener() { - public void onCellClicked(SourcesTableEvents sender, int row, - int cell) { - buildDayView(date); - } - }); - } - - @Override - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - super.updateFromUIDL(uidl, client); - // We want to draw our own hour list - realResolution = currentResolution; - currentResolution = RESOLUTION_DAY; - if (uidl.hasAttribute("min")) { - final String mins = uidl.getStringAttribute("min"); - final long min = (mins != null ? Long.parseLong(mins) : 0); - final String maxs = uidl.getStringAttribute("max"); - final long max = (maxs != null ? Long.parseLong(maxs) : 0); - final Date minDate = (min > 0 ? new Date(min) : null); - final Date maxDate = (max > 0 ? new Date(max) : null); - calPanel.setLimits(minDate, maxDate); - } - entrySource.clear(); - for (final Iterator cit = uidl.getChildIterator(); cit.hasNext();) { - final UIDL child = (UIDL) cit.next(); - if (child.getTag().equals("items")) { - for (final Iterator iit = child.getChildIterator(); iit - .hasNext();) { - final UIDL item = (UIDL) iit.next(); - entrySource.addItem(item); - } - break; - } - } - calPanel.updateCalendar(); - buildDayView(date); - } - - protected void buildDayView(Date date) { - if (hourPanel == null) { - hourPanel = new SimplePanel(); - hourPanel.setStyleName(CLASSNAME + "-hours"); - calPanel.getFlexCellFormatter().setColSpan(8, 0, 7); - calPanel.setWidget(8, 0, hourPanel); - } else { - hourPanel.clear(); - } - hourTable = new FlexTable(); - hourTable.addTableListener(ftListener); - hourPanel.add(hourTable); - hourTable.setCellSpacing(1); - - for (int i = 0; i < 24; i++) { - String style = (i % 2 == 0 ? "even" : "odd"); - if (realResolution >= RESOLUTION_HOUR) { - if (this.date != null && this.date.getHours() == i) { - style = "selected"; - } - } - hourTable.getRowFormatter().setStyleName(i, - CLASSNAME + "-row-" + style); - String hstr = (i < 10 ? "0" : "") + i + ":00"; - if (dts.isTwelveHourClock()) { - final String ampm = (i < 12 ? "am" : "pm"); - hstr = (i <= 12 ? i : i - 12) + ":00 " + ampm; - } - hourTable.setHTML(i, 0, "" + hstr + ""); - hourTable.getCellFormatter() - .setStyleName(i, 0, CLASSNAME + "-time"); - } - - final List entries = entrySource.getEntries(date, - DateTimeService.RESOLUTION_DAY); - int currentCol = 1; - for (final Iterator it = entries.iterator(); it.hasNext();) { - final CalendarEntry entry = (CalendarEntry) it.next(); - int start = 0; - int hours = 24; - if (!entry.isNotime()) { - Date d = entry.getStart(); - // TODO consider month&year as well - start = (d.getDate() < date.getDate() ? 0 : d.getHours()); - d = entry.getEnd(); - hours = (d.getDate() > date.getDate() ? 24 : d.getHours()) - - start; - if (hours < 1) { - // We can't draw entries smaller than - // one - hours = 1; - } - } - int col = currentCol; - if (col > 1) { - while (!hourTable.isCellPresent(start, col - 1)) { - col--; - } - } - hourTable.setHTML(start, col, "" - + (entry.getTitle() != null ? entry.getTitle() : " ") - + ""); - hourTable.getFlexCellFormatter().setRowSpan(start, col, hours); - hourTable.getFlexCellFormatter().setStyleName(start, col, - CLASSNAME + "-entry"); - final String sn = entry.getStyleName(); - if (sn != null && !sn.equals("")) { - hourTable.getFlexCellFormatter().addStyleName(start, col, - CLASSNAME + "-" + entry.getStyleName()); - } - final Element el = hourTable.getFlexCellFormatter().getElement( - start, col); - - String tooltip; - if (DateTimeService.isSameDay(entry.getStart(), entry.getEnd())) { - tooltip = (start < 10 ? "0" : "") + start + ":00"; - if (dts.isTwelveHourClock()) { - final String ampm = (start < 12 ? "am" : "pm"); - tooltip = (start <= 12 ? start : start - 12) + ":00 " - + ampm; - - } - tooltip += " (" + hours + "h) "; - if (entry.getTitle() != null) { - tooltip += entry.getTitle() + "\n "; - } - } else { - tooltip = entry.getStringForDate(entry.getEnd()) + "\n "; - } - if (entry.getDescription() != null) { - tooltip += "\"" + entry.getDescription() + "\""; - } - DOM.setElementProperty(el, "title", tooltip); - - currentCol++; - } - - // int hour = new Date().getHours()+1; // scroll to current hour - Date d = (this.date != null ? this.date : new Date()); - final int hour = d.getHours() + 1; // scroll to selected - // hour - final int h1 = hourPanel.getOffsetHeight() / 2; - final int oh = hourTable.getOffsetHeight(); - final int h2 = (int) (hour / 24.0 * oh); - final int scrollTop = h2 - h1; - final Element el = hourPanel.getElement(); - setScrollTop(el, scrollTop); - - } - - private native void setScrollTop(Element el, int scrollTop) - /*-{ - el.scrollTop = scrollTop; - }-*/; - - private class HourTableListener implements TableListener { - - public void onCellClicked(SourcesTableEvents sender, int row, int cell) { - if (realResolution < RESOLUTION_HOUR || date == null) { - return; - } - date.setHours(row); - client.updateVariable(id, "hour", row, immediate); - } - - } - - private class EntrySource implements ICalendarPanel.CalendarEntrySource { - - private final HashMap dates = new HashMap(); - - public void addItem(UIDL item) { - final String styleName = item.getStringAttribute("styleName"); - // final Integer id = new Integer(item.getIntAttribute("id")); - - DateTimeFormat dtf = DateTimeFormat - .getFormat("d MM yyyy HH:mm:ss Z"); - - Date startDate = dtf.parse(item.getStringAttribute("start")); - - // fix times with server-client difference - int diff = (startDate.getTimezoneOffset() - item - .getIntAttribute("Z")) * 60000; - startDate = new Date(startDate.getTime() + diff); - Date endDate; - if (item.hasAttribute("end")) { - endDate = dtf.parse(item.getStringAttribute("end")); - endDate = new Date(endDate.getTime() + diff); - } else { - endDate = (Date) startDate.clone(); - } - final String title = item.getStringAttribute("title"); - final String desc = item.getStringAttribute("description"); - final boolean notime = item.getBooleanAttribute("notime"); - final CalendarEntry entry = new CalendarEntry(styleName, startDate, - endDate, title, desc, notime); - - // TODO should remove+readd if the same entry (id) is - // added again - - for (final Date d = new Date(entry.getStart().getTime()); d - .getYear() <= entry.getEnd().getYear() - && d.getMonth() <= entry.getEnd().getYear() - && d.getDate() <= entry.getEnd().getDate(); d.setTime(d - .getTime() + 86400000)) { - final String key = d.getYear() + "" + d.getMonth() + "" - + d.getDate(); - ArrayList l = (ArrayList) dates.get(key); - if (l == null) { - l = new ArrayList(); - dates.put(key, l); - } - l.add(entry); - } - } - - public List getEntries(Date date, int resolution) { - final ArrayList res = new ArrayList(); - if (date == null) { - return res; - } - final List entries = (List) dates.get(date.getYear() + "" - + date.getMonth() + "" + date.getDate()); - - if (entries == null) { - return res; - } - for (final Iterator it = entries.iterator(); it.hasNext();) { - final CalendarEntry item = (CalendarEntry) it.next(); - if (DateTimeService.isInRange(date, item.getStart(), item - .getEnd(), resolution)) { - res.add(item); - } - } - - return res; - } - - public void clear() { - dates.clear(); - } - - } - -} diff --git a/src/com/vaadin/demo/reservation/gwt/client/ui/IGoogleMap.java b/src/com/vaadin/demo/reservation/gwt/client/ui/IGoogleMap.java deleted file mode 100644 index e69bcd16a9..0000000000 --- a/src/com/vaadin/demo/reservation/gwt/client/ui/IGoogleMap.java +++ /dev/null @@ -1,91 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.demo.reservation.gwt.client.ui; - -import java.util.Iterator; - -import com.google.gwt.maps.client.InfoWindowContent; -import com.google.gwt.maps.client.MapWidget; -import com.google.gwt.maps.client.control.SmallMapControl; -import com.google.gwt.maps.client.event.MarkerClickHandler; -import com.google.gwt.maps.client.geom.LatLng; -import com.google.gwt.maps.client.overlay.Marker; -import com.google.gwt.user.client.ui.Composite; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; - -public class IGoogleMap extends Composite implements Paintable { - - public static final String CLASSNAME = "i-googlemap"; - - private final MapWidget widget = new MapWidget(); - - public IGoogleMap() { - initWidget(widget); - setWidth("200px"); - setHeight("200px"); - setStyleName(CLASSNAME); - widget.addControl(new SmallMapControl()); - - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - widget.clearOverlays(); - LatLng pos = null; - for (final Iterator it = uidl.getChildIterator(); it.hasNext();) { - final UIDL u = (UIDL) it.next(); - if (u.getTag().equals("markers")) { - - for (final Iterator m = u.getChildIterator(); m.hasNext();) { - final UIDL umarker = (UIDL) m.next(); - final String html = "" - + umarker.getStringAttribute("html") + ""; - final double x = umarker.getDoubleAttribute("x"); - final double y = umarker.getDoubleAttribute("y"); - pos = LatLng.newInstance(x, y); - final Marker marker = new Marker(pos); - widget.addOverlay(marker); - if (html != null) { - addMarkerPopup(marker, html); - } - } - } - } - if (uidl.hasAttribute("width")) { - widget.setWidth(uidl.getStringAttribute("width")); - } - if (uidl.hasAttribute("height")) { - widget.setHeight(uidl.getStringAttribute("height")); - } - if (uidl.hasAttribute("zoom")) { - widget.setZoomLevel(uidl.getIntAttribute("zoom")); - } - if (uidl.hasAttribute("centerX") && uidl.hasAttribute("centerY")) { - final LatLng center = LatLng.newInstance(uidl - .getDoubleAttribute("centerX"), uidl - .getDoubleAttribute("centerY")); - widget.setCenter(center); - } else if (pos != null) { - // use last marker position - widget.setCenter(pos); - } - - } - - private void addMarkerPopup(Marker marker, final String html) { - marker.addMarkerClickHandler(new MarkerClickHandler() { - - public void onClick(MarkerClickEvent event) { - widget.getInfoWindow().open(event.getSender().getPoint(), - new InfoWindowContent(html)); - - } - - }); - - } - -} diff --git a/src/com/vaadin/demo/reservation/gwt/client/ui/VCalendarField.java b/src/com/vaadin/demo/reservation/gwt/client/ui/VCalendarField.java new file mode 100644 index 0000000000..b4082f7ed2 --- /dev/null +++ b/src/com/vaadin/demo/reservation/gwt/client/ui/VCalendarField.java @@ -0,0 +1,294 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.demo.reservation.gwt.client.ui; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +import com.google.gwt.i18n.client.DateTimeFormat; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.FlexTable; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.SourcesTableEvents; +import com.google.gwt.user.client.ui.TableListener; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.DateTimeService; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.ui.CalendarEntry; +import com.vaadin.terminal.gwt.client.ui.VCalendarPanel; +import com.vaadin.terminal.gwt.client.ui.VDateField; + +public class VCalendarField extends VDateField { + + private final VCalendarPanel calPanel; + + private SimplePanel hourPanel; + + private FlexTable hourTable; + + private final EntrySource entrySource; + + private final TableListener ftListener = new HourTableListener(); + + private int realResolution = RESOLUTION_DAY; + + private static final String CLASSNAME = VDateField.CLASSNAME + + "-entrycalendar"; + + public VCalendarField() { + super(); + setStyleName(CLASSNAME); + calPanel = new VCalendarPanel(this); + add(calPanel); + entrySource = new EntrySource(); + calPanel.setCalendarEntrySource(entrySource); + calPanel.addTableListener(new TableListener() { + public void onCellClicked(SourcesTableEvents sender, int row, + int cell) { + buildDayView(date); + } + }); + } + + @Override + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + super.updateFromUIDL(uidl, client); + // We want to draw our own hour list + realResolution = currentResolution; + currentResolution = RESOLUTION_DAY; + if (uidl.hasAttribute("min")) { + final String mins = uidl.getStringAttribute("min"); + final long min = (mins != null ? Long.parseLong(mins) : 0); + final String maxs = uidl.getStringAttribute("max"); + final long max = (maxs != null ? Long.parseLong(maxs) : 0); + final Date minDate = (min > 0 ? new Date(min) : null); + final Date maxDate = (max > 0 ? new Date(max) : null); + calPanel.setLimits(minDate, maxDate); + } + entrySource.clear(); + for (final Iterator cit = uidl.getChildIterator(); cit.hasNext();) { + final UIDL child = (UIDL) cit.next(); + if (child.getTag().equals("items")) { + for (final Iterator iit = child.getChildIterator(); iit + .hasNext();) { + final UIDL item = (UIDL) iit.next(); + entrySource.addItem(item); + } + break; + } + } + calPanel.updateCalendar(); + buildDayView(date); + } + + protected void buildDayView(Date date) { + if (hourPanel == null) { + hourPanel = new SimplePanel(); + hourPanel.setStyleName(CLASSNAME + "-hours"); + calPanel.getFlexCellFormatter().setColSpan(8, 0, 7); + calPanel.setWidget(8, 0, hourPanel); + } else { + hourPanel.clear(); + } + hourTable = new FlexTable(); + hourTable.addTableListener(ftListener); + hourPanel.add(hourTable); + hourTable.setCellSpacing(1); + + for (int i = 0; i < 24; i++) { + String style = (i % 2 == 0 ? "even" : "odd"); + if (realResolution >= RESOLUTION_HOUR) { + if (this.date != null && this.date.getHours() == i) { + style = "selected"; + } + } + hourTable.getRowFormatter().setStyleName(i, + CLASSNAME + "-row-" + style); + String hstr = (i < 10 ? "0" : "") + i + ":00"; + if (dts.isTwelveHourClock()) { + final String ampm = (i < 12 ? "am" : "pm"); + hstr = (i <= 12 ? i : i - 12) + ":00 " + ampm; + } + hourTable.setHTML(i, 0, "" + hstr + ""); + hourTable.getCellFormatter() + .setStyleName(i, 0, CLASSNAME + "-time"); + } + + final List entries = entrySource.getEntries(date, + DateTimeService.RESOLUTION_DAY); + int currentCol = 1; + for (final Iterator it = entries.iterator(); it.hasNext();) { + final CalendarEntry entry = (CalendarEntry) it.next(); + int start = 0; + int hours = 24; + if (!entry.isNotime()) { + Date d = entry.getStart(); + // TODO consider month&year as well + start = (d.getDate() < date.getDate() ? 0 : d.getHours()); + d = entry.getEnd(); + hours = (d.getDate() > date.getDate() ? 24 : d.getHours()) + - start; + if (hours < 1) { + // We can't draw entries smaller than + // one + hours = 1; + } + } + int col = currentCol; + if (col > 1) { + while (!hourTable.isCellPresent(start, col - 1)) { + col--; + } + } + hourTable.setHTML(start, col, "" + + (entry.getTitle() != null ? entry.getTitle() : " ") + + ""); + hourTable.getFlexCellFormatter().setRowSpan(start, col, hours); + hourTable.getFlexCellFormatter().setStyleName(start, col, + CLASSNAME + "-entry"); + final String sn = entry.getStyleName(); + if (sn != null && !sn.equals("")) { + hourTable.getFlexCellFormatter().addStyleName(start, col, + CLASSNAME + "-" + entry.getStyleName()); + } + final Element el = hourTable.getFlexCellFormatter().getElement( + start, col); + + String tooltip; + if (DateTimeService.isSameDay(entry.getStart(), entry.getEnd())) { + tooltip = (start < 10 ? "0" : "") + start + ":00"; + if (dts.isTwelveHourClock()) { + final String ampm = (start < 12 ? "am" : "pm"); + tooltip = (start <= 12 ? start : start - 12) + ":00 " + + ampm; + + } + tooltip += " (" + hours + "h) "; + if (entry.getTitle() != null) { + tooltip += entry.getTitle() + "\n "; + } + } else { + tooltip = entry.getStringForDate(entry.getEnd()) + "\n "; + } + if (entry.getDescription() != null) { + tooltip += "\"" + entry.getDescription() + "\""; + } + DOM.setElementProperty(el, "title", tooltip); + + currentCol++; + } + + // int hour = new Date().getHours()+1; // scroll to current hour + Date d = (this.date != null ? this.date : new Date()); + final int hour = d.getHours() + 1; // scroll to selected + // hour + final int h1 = hourPanel.getOffsetHeight() / 2; + final int oh = hourTable.getOffsetHeight(); + final int h2 = (int) (hour / 24.0 * oh); + final int scrollTop = h2 - h1; + final Element el = hourPanel.getElement(); + setScrollTop(el, scrollTop); + + } + + private native void setScrollTop(Element el, int scrollTop) + /*-{ + el.scrollTop = scrollTop; + }-*/; + + private class HourTableListener implements TableListener { + + public void onCellClicked(SourcesTableEvents sender, int row, int cell) { + if (realResolution < RESOLUTION_HOUR || date == null) { + return; + } + date.setHours(row); + client.updateVariable(id, "hour", row, immediate); + } + + } + + private class EntrySource implements VCalendarPanel.CalendarEntrySource { + + private final HashMap dates = new HashMap(); + + public void addItem(UIDL item) { + final String styleName = item.getStringAttribute("styleName"); + // final Integer id = new Integer(item.getIntAttribute("id")); + + DateTimeFormat dtf = DateTimeFormat + .getFormat("d MM yyyy HH:mm:ss Z"); + + Date startDate = dtf.parse(item.getStringAttribute("start")); + + // fix times with server-client difference + int diff = (startDate.getTimezoneOffset() - item + .getIntAttribute("Z")) * 60000; + startDate = new Date(startDate.getTime() + diff); + Date endDate; + if (item.hasAttribute("end")) { + endDate = dtf.parse(item.getStringAttribute("end")); + endDate = new Date(endDate.getTime() + diff); + } else { + endDate = (Date) startDate.clone(); + } + final String title = item.getStringAttribute("title"); + final String desc = item.getStringAttribute("description"); + final boolean notime = item.getBooleanAttribute("notime"); + final CalendarEntry entry = new CalendarEntry(styleName, startDate, + endDate, title, desc, notime); + + // TODO should remove+readd if the same entry (id) is + // added again + + for (final Date d = new Date(entry.getStart().getTime()); d + .getYear() <= entry.getEnd().getYear() + && d.getMonth() <= entry.getEnd().getYear() + && d.getDate() <= entry.getEnd().getDate(); d.setTime(d + .getTime() + 86400000)) { + final String key = d.getYear() + "" + d.getMonth() + "" + + d.getDate(); + ArrayList l = (ArrayList) dates.get(key); + if (l == null) { + l = new ArrayList(); + dates.put(key, l); + } + l.add(entry); + } + } + + public List getEntries(Date date, int resolution) { + final ArrayList res = new ArrayList(); + if (date == null) { + return res; + } + final List entries = (List) dates.get(date.getYear() + "" + + date.getMonth() + "" + date.getDate()); + + if (entries == null) { + return res; + } + for (final Iterator it = entries.iterator(); it.hasNext();) { + final CalendarEntry item = (CalendarEntry) it.next(); + if (DateTimeService.isInRange(date, item.getStart(), item + .getEnd(), resolution)) { + res.add(item); + } + } + + return res; + } + + public void clear() { + dates.clear(); + } + + } + +} diff --git a/src/com/vaadin/demo/reservation/gwt/client/ui/VGoogleMap.java b/src/com/vaadin/demo/reservation/gwt/client/ui/VGoogleMap.java new file mode 100644 index 0000000000..b2c8c1b403 --- /dev/null +++ b/src/com/vaadin/demo/reservation/gwt/client/ui/VGoogleMap.java @@ -0,0 +1,91 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.demo.reservation.gwt.client.ui; + +import java.util.Iterator; + +import com.google.gwt.maps.client.InfoWindowContent; +import com.google.gwt.maps.client.MapWidget; +import com.google.gwt.maps.client.control.SmallMapControl; +import com.google.gwt.maps.client.event.MarkerClickHandler; +import com.google.gwt.maps.client.geom.LatLng; +import com.google.gwt.maps.client.overlay.Marker; +import com.google.gwt.user.client.ui.Composite; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; + +public class VGoogleMap extends Composite implements Paintable { + + public static final String CLASSNAME = "i-googlemap"; + + private final MapWidget widget = new MapWidget(); + + public VGoogleMap() { + initWidget(widget); + setWidth("200px"); + setHeight("200px"); + setStyleName(CLASSNAME); + widget.addControl(new SmallMapControl()); + + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + widget.clearOverlays(); + LatLng pos = null; + for (final Iterator it = uidl.getChildIterator(); it.hasNext();) { + final UIDL u = (UIDL) it.next(); + if (u.getTag().equals("markers")) { + + for (final Iterator m = u.getChildIterator(); m.hasNext();) { + final UIDL umarker = (UIDL) m.next(); + final String html = "" + + umarker.getStringAttribute("html") + ""; + final double x = umarker.getDoubleAttribute("x"); + final double y = umarker.getDoubleAttribute("y"); + pos = LatLng.newInstance(x, y); + final Marker marker = new Marker(pos); + widget.addOverlay(marker); + if (html != null) { + addMarkerPopup(marker, html); + } + } + } + } + if (uidl.hasAttribute("width")) { + widget.setWidth(uidl.getStringAttribute("width")); + } + if (uidl.hasAttribute("height")) { + widget.setHeight(uidl.getStringAttribute("height")); + } + if (uidl.hasAttribute("zoom")) { + widget.setZoomLevel(uidl.getIntAttribute("zoom")); + } + if (uidl.hasAttribute("centerX") && uidl.hasAttribute("centerY")) { + final LatLng center = LatLng.newInstance(uidl + .getDoubleAttribute("centerX"), uidl + .getDoubleAttribute("centerY")); + widget.setCenter(center); + } else if (pos != null) { + // use last marker position + widget.setCenter(pos); + } + + } + + private void addMarkerPopup(Marker marker, final String html) { + marker.addMarkerClickHandler(new MarkerClickHandler() { + + public void onClick(MarkerClickEvent event) { + widget.getInfoWindow().open(event.getSender().getPoint(), + new InfoWindowContent(html)); + + } + + }); + + } + +} diff --git a/src/com/vaadin/demo/sampler/gwt/client/SamplerWidgetSet.java b/src/com/vaadin/demo/sampler/gwt/client/SamplerWidgetSet.java index 2738b21990..24dde94a3c 100644 --- a/src/com/vaadin/demo/sampler/gwt/client/SamplerWidgetSet.java +++ b/src/com/vaadin/demo/sampler/gwt/client/SamplerWidgetSet.java @@ -1,8 +1,8 @@ package com.vaadin.demo.sampler.gwt.client; -import com.vaadin.demo.sampler.gwt.client.ui.IActiveLink; -import com.vaadin.demo.sampler.gwt.client.ui.ICodeLabel; -import com.vaadin.demo.sampler.gwt.client.ui.IGoogleAnalytics; +import com.vaadin.demo.sampler.gwt.client.ui.VActiveLink; +import com.vaadin.demo.sampler.gwt.client.ui.VCodeLabel; +import com.vaadin.demo.sampler.gwt.client.ui.VGoogleAnalytics; import com.vaadin.terminal.gwt.client.DefaultWidgetSet; import com.vaadin.terminal.gwt.client.Paintable; import com.vaadin.terminal.gwt.client.UIDL; @@ -12,12 +12,12 @@ public class SamplerWidgetSet extends DefaultWidgetSet { @Override public Paintable createWidget(UIDL uidl) { final Class classType = resolveWidgetType(uidl); - if (IGoogleAnalytics.class == classType) { - return new IGoogleAnalytics(); - } else if (ICodeLabel.class == classType) { - return new ICodeLabel(); - } else if (IActiveLink.class == classType) { - return new IActiveLink(); + if (VGoogleAnalytics.class == classType) { + return new VGoogleAnalytics(); + } else if (VCodeLabel.class == classType) { + return new VCodeLabel(); + } else if (VActiveLink.class == classType) { + return new VActiveLink(); } else { return super.createWidget(uidl); } @@ -27,11 +27,11 @@ public class SamplerWidgetSet extends DefaultWidgetSet { protected Class resolveWidgetType(UIDL uidl) { final String tag = uidl.getTag(); if ("googleanalytics".equals(tag)) { - return IGoogleAnalytics.class; + return VGoogleAnalytics.class; } else if ("codelabel".equals(tag)) { - return ICodeLabel.class; + return VCodeLabel.class; } else if ("activelink".equals(tag)) { - return IActiveLink.class; + return VActiveLink.class; } else { return super.resolveWidgetType(uidl); } diff --git a/src/com/vaadin/demo/sampler/gwt/client/ui/IActiveLink.java b/src/com/vaadin/demo/sampler/gwt/client/ui/IActiveLink.java deleted file mode 100644 index d539ef5611..0000000000 --- a/src/com/vaadin/demo/sampler/gwt/client/ui/IActiveLink.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.vaadin.demo.sampler.gwt.client.ui; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.MouseListener; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.ui.ILink; - -public class IActiveLink extends ILink { - - String id; - ApplicationConnection client; - boolean listening = false; - - public IActiveLink() { - addMouseListener(new MouseListener() { - public void onMouseDown(Widget sender, int x, int y) { - } - - public void onMouseEnter(Widget sender) { - } - - public void onMouseLeave(Widget sender) { - } - - public void onMouseMove(Widget sender, int x, int y) { - } - - public void onMouseUp(Widget sender, int x, int y) { - Event e = DOM.eventGetCurrentEvent(); - if (e.getButton() == Event.BUTTON_MIDDLE) { - sendVariables(); - } - } - }); - } - - /** - * Sends variables, returns true if default handler should be called (i.e if - * server is listening and the link was claimed to be opened by the client) - * - * @return - */ - private boolean sendVariables() { - Event e = DOM.eventGetCurrentEvent(); - boolean opened = (e.getCtrlKey() || e.getAltKey() || e.getShiftKey() - || e.getMetaKey() || e.getButton() == Event.BUTTON_MIDDLE); - - // Works as ILink if no-one is listening - if (listening) { - if (opened) { - // ILink will open, notify server - client.updateVariable(id, "opened", true, false); - } else { - e.preventDefault(); - } - client.updateVariable(id, "activated", true, true); - } - return !listening || opened; - } - - @Override - public void onClick(Widget sender) { - - if (sendVariables()) { - // run default if not listening, or we claimed link was opened - super.onClick(sender); - } - } - - @Override - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - // Ensure correct implementation, - // but don't let container manage caption etc. - if (client.updateComponent(this, uidl, false)) { - return; - } - - // Save details - this.client = client; - id = uidl.getId(); - listening = uidl.hasVariable("activated"); - - super.updateFromUIDL(uidl, client); - } - -} diff --git a/src/com/vaadin/demo/sampler/gwt/client/ui/ICodeLabel.java b/src/com/vaadin/demo/sampler/gwt/client/ui/ICodeLabel.java deleted file mode 100644 index a97d74e067..0000000000 --- a/src/com/vaadin/demo/sampler/gwt/client/ui/ICodeLabel.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.vaadin.demo.sampler.gwt.client.ui; - -import com.google.gwt.dom.client.Element; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.ui.ILabel; - -public class ICodeLabel extends ILabel { - - public ICodeLabel() { - super(); - } - - @Override - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - super.updateFromUIDL(uidl, client); - Element pre = getElement().getFirstChildElement(); - if (null != pre) { - pre.setClassName("prettyprint"); - prettyPrint(); - } - } - - private native void prettyPrint() - /*-{ - $wnd.prettyPrint(); - }-*/; - -} diff --git a/src/com/vaadin/demo/sampler/gwt/client/ui/IGoogleAnalytics.java b/src/com/vaadin/demo/sampler/gwt/client/ui/IGoogleAnalytics.java deleted file mode 100644 index 13e15fb32c..0000000000 --- a/src/com/vaadin/demo/sampler/gwt/client/ui/IGoogleAnalytics.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.vaadin.demo.sampler.gwt.client.ui; - -import com.google.gwt.dom.client.Document; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; - -public class IGoogleAnalytics extends Widget implements Paintable { - - public IGoogleAnalytics() { - setElement(Document.get().createDivElement()); - if (BrowserInfo.get().isIE6()) { - getElement().getStyle().setProperty("overflow", "hidden"); - getElement().getStyle().setProperty("height", "0"); - getElement().getStyle().setProperty("width", "0"); - } - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - if (isLocalHostUrl()) { - // Do not track localhost page views - return; - } - String trackerId = uidl.getStringAttribute("trackerid"); - String pageId = uidl.getStringAttribute("pageid"); - String domainName = uidl.getStringAttribute("domain"); - - String res = trackPageview(trackerId, pageId, domainName); - if (null != res) { - ApplicationConnection.getConsole().log( - "WebAnalytics.trackPageview(" + trackerId + "," + pageId - + "," + domainName + ") FAILED: " + res); - } else { - ApplicationConnection.getConsole().log( - "WebAnalytics.trackPageview(" + trackerId + "," + pageId - + "," + domainName + ") SUCCESS."); - } - } - - private native boolean isLocalHostUrl() - /*-{ - var location = $wnd.location; - var re = /^http:\/\/(localhost|127.0.0.1)/; - return re.test(location); - }-*/; - - private native String trackPageview(String trackerId, String pageId, - String domainName) - /*-{ - if (!$wnd._gat) { - return "Tracker not found (running offline?)"; - } - try { - var pageTracker = $wnd._gat._getTracker(trackerId); - if (domainName) { - pageTracker._setDomainName(domainName); - } - if (pageId) { - pageTracker._trackPageview(pageId); - } else { - pageTracker._trackPageview(); - } - return null; - } catch(err) { - return ""+err; - } - }-*/; -} diff --git a/src/com/vaadin/demo/sampler/gwt/client/ui/VActiveLink.java b/src/com/vaadin/demo/sampler/gwt/client/ui/VActiveLink.java new file mode 100644 index 0000000000..a95db45e08 --- /dev/null +++ b/src/com/vaadin/demo/sampler/gwt/client/ui/VActiveLink.java @@ -0,0 +1,89 @@ +package com.vaadin.demo.sampler.gwt.client.ui; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.MouseListener; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.ui.VLink; + +public class VActiveLink extends VLink { + + String id; + ApplicationConnection client; + boolean listening = false; + + public VActiveLink() { + addMouseListener(new MouseListener() { + public void onMouseDown(Widget sender, int x, int y) { + } + + public void onMouseEnter(Widget sender) { + } + + public void onMouseLeave(Widget sender) { + } + + public void onMouseMove(Widget sender, int x, int y) { + } + + public void onMouseUp(Widget sender, int x, int y) { + Event e = DOM.eventGetCurrentEvent(); + if (e.getButton() == Event.BUTTON_MIDDLE) { + sendVariables(); + } + } + }); + } + + /** + * Sends variables, returns true if default handler should be called (i.e if + * server is listening and the link was claimed to be opened by the client) + * + * @return + */ + private boolean sendVariables() { + Event e = DOM.eventGetCurrentEvent(); + boolean opened = (e.getCtrlKey() || e.getAltKey() || e.getShiftKey() + || e.getMetaKey() || e.getButton() == Event.BUTTON_MIDDLE); + + // Works as VLink if no-one is listening + if (listening) { + if (opened) { + // VLink will open, notify server + client.updateVariable(id, "opened", true, false); + } else { + e.preventDefault(); + } + client.updateVariable(id, "activated", true, true); + } + return !listening || opened; + } + + @Override + public void onClick(Widget sender) { + + if (sendVariables()) { + // run default if not listening, or we claimed link was opened + super.onClick(sender); + } + } + + @Override + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + // Ensure correct implementation, + // but don't let container manage caption etc. + if (client.updateComponent(this, uidl, false)) { + return; + } + + // Save details + this.client = client; + id = uidl.getId(); + listening = uidl.hasVariable("activated"); + + super.updateFromUIDL(uidl, client); + } + +} diff --git a/src/com/vaadin/demo/sampler/gwt/client/ui/VCodeLabel.java b/src/com/vaadin/demo/sampler/gwt/client/ui/VCodeLabel.java new file mode 100644 index 0000000000..e4ba51dd4c --- /dev/null +++ b/src/com/vaadin/demo/sampler/gwt/client/ui/VCodeLabel.java @@ -0,0 +1,29 @@ +package com.vaadin.demo.sampler.gwt.client.ui; + +import com.google.gwt.dom.client.Element; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.ui.VLabel; + +public class VCodeLabel extends VLabel { + + public VCodeLabel() { + super(); + } + + @Override + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + super.updateFromUIDL(uidl, client); + Element pre = getElement().getFirstChildElement(); + if (null != pre) { + pre.setClassName("prettyprint"); + prettyPrint(); + } + } + + private native void prettyPrint() + /*-{ + $wnd.prettyPrint(); + }-*/; + +} diff --git a/src/com/vaadin/demo/sampler/gwt/client/ui/VGoogleAnalytics.java b/src/com/vaadin/demo/sampler/gwt/client/ui/VGoogleAnalytics.java new file mode 100644 index 0000000000..263a8af838 --- /dev/null +++ b/src/com/vaadin/demo/sampler/gwt/client/ui/VGoogleAnalytics.java @@ -0,0 +1,70 @@ +package com.vaadin.demo.sampler.gwt.client.ui; + +import com.google.gwt.dom.client.Document; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; + +public class VGoogleAnalytics extends Widget implements Paintable { + + public VGoogleAnalytics() { + setElement(Document.get().createDivElement()); + if (BrowserInfo.get().isIE6()) { + getElement().getStyle().setProperty("overflow", "hidden"); + getElement().getStyle().setProperty("height", "0"); + getElement().getStyle().setProperty("width", "0"); + } + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + if (isLocalHostUrl()) { + // Do not track localhost page views + return; + } + String trackerId = uidl.getStringAttribute("trackerid"); + String pageId = uidl.getStringAttribute("pageid"); + String domainName = uidl.getStringAttribute("domain"); + + String res = trackPageview(trackerId, pageId, domainName); + if (null != res) { + ApplicationConnection.getConsole().log( + "WebAnalytics.trackPageview(" + trackerId + "," + pageId + + "," + domainName + ") FAILED: " + res); + } else { + ApplicationConnection.getConsole().log( + "WebAnalytics.trackPageview(" + trackerId + "," + pageId + + "," + domainName + ") SUCCESS."); + } + } + + private native boolean isLocalHostUrl() + /*-{ + var location = $wnd.location; + var re = /^http:\/\/(localhost|127.0.0.1)/; + return re.test(location); + }-*/; + + private native String trackPageview(String trackerId, String pageId, + String domainName) + /*-{ + if (!$wnd._gat) { + return "Tracker not found (running offline?)"; + } + try { + var pageTracker = $wnd._gat._getTracker(trackerId); + if (domainName) { + pageTracker._setDomainName(domainName); + } + if (pageId) { + pageTracker._trackPageview(pageId); + } else { + pageTracker._trackPageview(); + } + return null; + } catch(err) { + return ""+err; + } + }-*/; +} diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java index e4bb48c9ba..7324b12613 100755 --- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java +++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java @@ -38,10 +38,10 @@ import com.google.gwt.user.client.ui.Widget; import com.vaadin.terminal.gwt.client.RenderInformation.FloatSize; import com.vaadin.terminal.gwt.client.RenderInformation.Size; import com.vaadin.terminal.gwt.client.ui.Field; -import com.vaadin.terminal.gwt.client.ui.IContextMenu; -import com.vaadin.terminal.gwt.client.ui.INotification; -import com.vaadin.terminal.gwt.client.ui.IView; -import com.vaadin.terminal.gwt.client.ui.INotification.HideEvent; +import com.vaadin.terminal.gwt.client.ui.VContextMenu; +import com.vaadin.terminal.gwt.client.ui.VNotification; +import com.vaadin.terminal.gwt.client.ui.VView; +import com.vaadin.terminal.gwt.client.ui.VNotification.HideEvent; /** * Entry point classes define onModuleLoad(). @@ -78,14 +78,14 @@ public class ApplicationConnection { private final WidgetSet widgetSet; - private IContextMenu contextMenu = null; + private VContextMenu contextMenu = null; private Timer loadTimer; private Timer loadTimer2; private Timer loadTimer3; private Element loadElement; - private final IView view; + private final VView view; private boolean applicationRunning = false; @@ -120,7 +120,7 @@ public class ApplicationConnection { configuration = cnf; windowName = configuration.getInitialWindowName(); if (isDebugMode()) { - console = new IDebugConsole(this, cnf, !isQuietDebugMode()); + console = new VDebugConsole(this, cnf, !isQuietDebugMode()); } else { console = new NullConsole(); } @@ -135,7 +135,7 @@ public class ApplicationConnection { initializeClientHooks(); - view = new IView(cnf.getRootPanelId()); + view = new VView(cnf.getRootPanelId()); showLoadingIndicator(); } @@ -386,12 +386,12 @@ public class ApplicationConnection { + "

"; } if (html.length() > 0) { - INotification n = new INotification(1000 * 60 * 45); + VNotification n = new VNotification(1000 * 60 * 45); n.addEventListener(new NotificationRedirect(configuration .getCommunicationErrorUrl())); n - .show(html, INotification.CENTERED_TOP, - INotification.STYLE_SYSTEM); + .show(html, VNotification.CENTERED_TOP, + VNotification.STYLE_SYSTEM); } else { redirect(configuration.getCommunicationErrorUrl()); } @@ -694,10 +694,10 @@ public class ApplicationConnection { if (html.length() != 0) { /* 45 min */ - INotification n = new INotification(1000 * 60 * 45); + VNotification n = new VNotification(1000 * 60 * 45); n.addEventListener(new NotificationRedirect(url)); - n.show(html, INotification.CENTERED_TOP, - INotification.STYLE_SYSTEM); + n.show(html, VNotification.CENTERED_TOP, + VNotification.STYLE_SYSTEM); } else { redirect(url); } @@ -1461,11 +1461,11 @@ public class ApplicationConnection { /** * Singleton method to get instance of app's context menu. * - * @return IContextMenu object + * @return VContextMenu object */ - public IContextMenu getContextMenu() { + public VContextMenu getContextMenu() { if (contextMenu == null) { - contextMenu = new IContextMenu(); + contextMenu = new VContextMenu(); DOM.setElementProperty(contextMenu.getElement(), "id", "PID_TOOLKIT_CM"); } @@ -1506,7 +1506,7 @@ public class ApplicationConnection { * messages, such as session expired. * */ - private class NotificationRedirect implements INotification.EventListener { + private class NotificationRedirect implements VNotification.EventListener { String url; NotificationRedirect(String url) { @@ -1540,7 +1540,7 @@ public class ApplicationConnection { } } - private final ITooltip tooltip = new ITooltip(this); + private final VTooltip tooltip = new VTooltip(this); /** * Component may want to delegate Tooltip handling to client. Layouts add @@ -1626,7 +1626,7 @@ public class ApplicationConnection { makeUidlRequest("", true, false, true); } - public IView getView() { + public VView getView() { return view; } diff --git a/src/com/vaadin/terminal/gwt/client/ComponentLocator.java b/src/com/vaadin/terminal/gwt/client/ComponentLocator.java index ccd0022876..82ec1a4735 100644 --- a/src/com/vaadin/terminal/gwt/client/ComponentLocator.java +++ b/src/com/vaadin/terminal/gwt/client/ComponentLocator.java @@ -7,8 +7,8 @@ import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.ui.HasWidgets; import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ui.IView; -import com.vaadin.terminal.gwt.client.ui.IWindow; +import com.vaadin.terminal.gwt.client.ui.VView; +import com.vaadin.terminal.gwt.client.ui.VWindow; import com.vaadin.terminal.gwt.client.ui.SubPartAware; /** @@ -71,8 +71,8 @@ public class ComponentLocator { String subPartName = client.getContextMenu().getSubPartName( targetElement); if (subPartName != null) { - // IContextMenu, singleton attached directly to rootpanel - return "/IContextMenu[0]" + SUBPART_SEPARATOR + subPartName; + // VContextMenu, singleton attached directly to rootpanel + return "/VContextMenu[0]" + SUBPART_SEPARATOR + subPartName; } return null; @@ -226,14 +226,14 @@ public class ComponentLocator { return pid; } - if (w instanceof IView) { + if (w instanceof VView) { return ""; - } else if (w instanceof IWindow) { - IWindow win = (IWindow) w; - ArrayList subWindowList = client.getView() + } else if (w instanceof VWindow) { + VWindow win = (VWindow) w; + ArrayList subWindowList = client.getView() .getSubWindowList(); int indexOfSubWindow = subWindowList.indexOf(win); - return PARENTCHILD_SEPARATOR + "IWindow[" + indexOfSubWindow + "]"; + return PARENTCHILD_SEPARATOR + "VWindow[" + indexOfSubWindow + "]"; } Widget parent = w.getParent(); @@ -283,9 +283,9 @@ public class ComponentLocator { Iterator i; String widgetClassName = split[0]; - if (widgetClassName.equals("IWindow")) { + if (widgetClassName.equals("VWindow")) { i = client.getView().getSubWindowList().iterator(); - } else if (widgetClassName.equals("IContextMenu")) { + } else if (widgetClassName.equals("VContextMenu")) { return client.getContextMenu(); } else { i = parent.iterator(); diff --git a/src/com/vaadin/terminal/gwt/client/DefaultWidgetSet.java b/src/com/vaadin/terminal/gwt/client/DefaultWidgetSet.java index 104ca1d232..9ac2337004 100644 --- a/src/com/vaadin/terminal/gwt/client/DefaultWidgetSet.java +++ b/src/com/vaadin/terminal/gwt/client/DefaultWidgetSet.java @@ -5,48 +5,48 @@ package com.vaadin.terminal.gwt.client; import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ui.IAbsoluteLayout; -import com.vaadin.terminal.gwt.client.ui.IAccordion; -import com.vaadin.terminal.gwt.client.ui.IButton; -import com.vaadin.terminal.gwt.client.ui.ICheckBox; -import com.vaadin.terminal.gwt.client.ui.ICustomComponent; -import com.vaadin.terminal.gwt.client.ui.ICustomLayout; -import com.vaadin.terminal.gwt.client.ui.IDateFieldCalendar; -import com.vaadin.terminal.gwt.client.ui.IEmbedded; -import com.vaadin.terminal.gwt.client.ui.IFilterSelect; -import com.vaadin.terminal.gwt.client.ui.IForm; -import com.vaadin.terminal.gwt.client.ui.IFormLayout; -import com.vaadin.terminal.gwt.client.ui.IGridLayout; -import com.vaadin.terminal.gwt.client.ui.IHorizontalLayout; -import com.vaadin.terminal.gwt.client.ui.ILabel; -import com.vaadin.terminal.gwt.client.ui.ILink; -import com.vaadin.terminal.gwt.client.ui.IListSelect; -import com.vaadin.terminal.gwt.client.ui.IMenuBar; -import com.vaadin.terminal.gwt.client.ui.INativeSelect; -import com.vaadin.terminal.gwt.client.ui.IOptionGroup; -import com.vaadin.terminal.gwt.client.ui.IOrderedLayout; -import com.vaadin.terminal.gwt.client.ui.IPanel; -import com.vaadin.terminal.gwt.client.ui.IPasswordField; -import com.vaadin.terminal.gwt.client.ui.IPopupCalendar; -import com.vaadin.terminal.gwt.client.ui.IPopupView; -import com.vaadin.terminal.gwt.client.ui.IProgressIndicator; -import com.vaadin.terminal.gwt.client.ui.IScrollTable; -import com.vaadin.terminal.gwt.client.ui.ISlider; -import com.vaadin.terminal.gwt.client.ui.ISplitPanelHorizontal; -import com.vaadin.terminal.gwt.client.ui.ISplitPanelVertical; -import com.vaadin.terminal.gwt.client.ui.ITablePaging; -import com.vaadin.terminal.gwt.client.ui.ITabsheet; -import com.vaadin.terminal.gwt.client.ui.ITextArea; -import com.vaadin.terminal.gwt.client.ui.ITextField; -import com.vaadin.terminal.gwt.client.ui.ITextualDate; -import com.vaadin.terminal.gwt.client.ui.ITree; -import com.vaadin.terminal.gwt.client.ui.ITwinColSelect; -import com.vaadin.terminal.gwt.client.ui.IUnknownComponent; -import com.vaadin.terminal.gwt.client.ui.IUpload; -import com.vaadin.terminal.gwt.client.ui.IUriFragmentUtility; -import com.vaadin.terminal.gwt.client.ui.IVerticalLayout; -import com.vaadin.terminal.gwt.client.ui.IWindow; -import com.vaadin.terminal.gwt.client.ui.richtextarea.IRichTextArea; +import com.vaadin.terminal.gwt.client.ui.VAbsoluteLayout; +import com.vaadin.terminal.gwt.client.ui.VAccordion; +import com.vaadin.terminal.gwt.client.ui.VButton; +import com.vaadin.terminal.gwt.client.ui.VCheckBox; +import com.vaadin.terminal.gwt.client.ui.VCustomComponent; +import com.vaadin.terminal.gwt.client.ui.VCustomLayout; +import com.vaadin.terminal.gwt.client.ui.VDateFieldCalendar; +import com.vaadin.terminal.gwt.client.ui.VEmbedded; +import com.vaadin.terminal.gwt.client.ui.VFilterSelect; +import com.vaadin.terminal.gwt.client.ui.VForm; +import com.vaadin.terminal.gwt.client.ui.VFormLayout; +import com.vaadin.terminal.gwt.client.ui.VGridLayout; +import com.vaadin.terminal.gwt.client.ui.VHorizontalLayout; +import com.vaadin.terminal.gwt.client.ui.VLabel; +import com.vaadin.terminal.gwt.client.ui.VLink; +import com.vaadin.terminal.gwt.client.ui.VListSelect; +import com.vaadin.terminal.gwt.client.ui.VMenuBar; +import com.vaadin.terminal.gwt.client.ui.VNativeSelect; +import com.vaadin.terminal.gwt.client.ui.VOptionGroup; +import com.vaadin.terminal.gwt.client.ui.VOrderedLayout; +import com.vaadin.terminal.gwt.client.ui.VPanel; +import com.vaadin.terminal.gwt.client.ui.VPasswordField; +import com.vaadin.terminal.gwt.client.ui.VPopupCalendar; +import com.vaadin.terminal.gwt.client.ui.VPopupView; +import com.vaadin.terminal.gwt.client.ui.VProgressIndicator; +import com.vaadin.terminal.gwt.client.ui.VScrollTable; +import com.vaadin.terminal.gwt.client.ui.VSlider; +import com.vaadin.terminal.gwt.client.ui.VSplitPanelHorizontal; +import com.vaadin.terminal.gwt.client.ui.VSplitPanelVertical; +import com.vaadin.terminal.gwt.client.ui.VTablePaging; +import com.vaadin.terminal.gwt.client.ui.VTabsheet; +import com.vaadin.terminal.gwt.client.ui.VTextArea; +import com.vaadin.terminal.gwt.client.ui.VTextField; +import com.vaadin.terminal.gwt.client.ui.VTextualDate; +import com.vaadin.terminal.gwt.client.ui.VTree; +import com.vaadin.terminal.gwt.client.ui.VTwinColSelect; +import com.vaadin.terminal.gwt.client.ui.VUnknownComponent; +import com.vaadin.terminal.gwt.client.ui.VUpload; +import com.vaadin.terminal.gwt.client.ui.VUriFragmentUtility; +import com.vaadin.terminal.gwt.client.ui.VVerticalLayout; +import com.vaadin.terminal.gwt.client.ui.VWindow; +import com.vaadin.terminal.gwt.client.ui.richtextarea.VRichTextArea; public class DefaultWidgetSet implements WidgetSet { @@ -60,91 +60,91 @@ public class DefaultWidgetSet implements WidgetSet { public Paintable createWidget(UIDL uidl) { final Class classType = resolveWidgetType(uidl); - if (ICheckBox.class == classType) { - return new ICheckBox(); - } else if (IButton.class == classType) { - return new IButton(); - } else if (IWindow.class == classType) { - return new IWindow(); - } else if (IOrderedLayout.class == classType) { - return new IOrderedLayout(); - } else if (IVerticalLayout.class == classType) { - return new IVerticalLayout(); - } else if (IHorizontalLayout.class == classType) { - return new IHorizontalLayout(); - } else if (ILabel.class == classType) { - return new ILabel(); - } else if (ILink.class == classType) { - return new ILink(); - } else if (IGridLayout.class == classType) { - return new IGridLayout(); - } else if (ITree.class == classType) { - return new ITree(); - } else if (IOptionGroup.class == classType) { - return new IOptionGroup(); - } else if (ITwinColSelect.class == classType) { - return new ITwinColSelect(); - } else if (INativeSelect.class == classType) { - return new INativeSelect(); - } else if (IListSelect.class == classType) { - return new IListSelect(); - } else if (IPanel.class == classType) { - return new IPanel(); - } else if (ITabsheet.class == classType) { - return new ITabsheet(); - } else if (IEmbedded.class == classType) { - return new IEmbedded(); - } else if (ICustomLayout.class == classType) { - return new ICustomLayout(); - } else if (ICustomComponent.class == classType) { - return new ICustomComponent(); - } else if (ITextArea.class == classType) { - return new ITextArea(); - } else if (IPasswordField.class == classType) { - return new IPasswordField(); - } else if (ITextField.class == classType) { - return new ITextField(); - } else if (ITablePaging.class == classType) { - return new ITablePaging(); - } else if (IScrollTable.class == classType) { - return new IScrollTable(); - } else if (IDateFieldCalendar.class == classType) { - return new IDateFieldCalendar(); - } else if (ITextualDate.class == classType) { - return new ITextualDate(); - } else if (IPopupCalendar.class == classType) { - return new IPopupCalendar(); - } else if (ISlider.class == classType) { - return new ISlider(); - } else if (IForm.class == classType) { - return new IForm(); - } else if (IFormLayout.class == classType) { - return new IFormLayout(); - } else if (IUpload.class == classType) { - return new IUpload(); - } else if (ISplitPanelHorizontal.class == classType) { - return new ISplitPanelHorizontal(); - } else if (ISplitPanelVertical.class == classType) { - return new ISplitPanelVertical(); - } else if (IFilterSelect.class == classType) { - return new IFilterSelect(); - } else if (IProgressIndicator.class == classType) { - return new IProgressIndicator(); - } else if (IRichTextArea.class == classType) { - return new IRichTextArea(); - } else if (IAccordion.class == classType) { - return new IAccordion(); - } else if (IMenuBar.class == classType) { - return new IMenuBar(); - } else if (IPopupView.class == classType) { - return new IPopupView(); - } else if (IUriFragmentUtility.class == classType) { - return new IUriFragmentUtility(); - } else if (IAbsoluteLayout.class == classType) { - return new IAbsoluteLayout(); + if (VCheckBox.class == classType) { + return new VCheckBox(); + } else if (VButton.class == classType) { + return new VButton(); + } else if (VWindow.class == classType) { + return new VWindow(); + } else if (VOrderedLayout.class == classType) { + return new VOrderedLayout(); + } else if (VVerticalLayout.class == classType) { + return new VVerticalLayout(); + } else if (VHorizontalLayout.class == classType) { + return new VHorizontalLayout(); + } else if (VLabel.class == classType) { + return new VLabel(); + } else if (VLink.class == classType) { + return new VLink(); + } else if (VGridLayout.class == classType) { + return new VGridLayout(); + } else if (VTree.class == classType) { + return new VTree(); + } else if (VOptionGroup.class == classType) { + return new VOptionGroup(); + } else if (VTwinColSelect.class == classType) { + return new VTwinColSelect(); + } else if (VNativeSelect.class == classType) { + return new VNativeSelect(); + } else if (VListSelect.class == classType) { + return new VListSelect(); + } else if (VPanel.class == classType) { + return new VPanel(); + } else if (VTabsheet.class == classType) { + return new VTabsheet(); + } else if (VEmbedded.class == classType) { + return new VEmbedded(); + } else if (VCustomLayout.class == classType) { + return new VCustomLayout(); + } else if (VCustomComponent.class == classType) { + return new VCustomComponent(); + } else if (VTextArea.class == classType) { + return new VTextArea(); + } else if (VPasswordField.class == classType) { + return new VPasswordField(); + } else if (VTextField.class == classType) { + return new VTextField(); + } else if (VTablePaging.class == classType) { + return new VTablePaging(); + } else if (VScrollTable.class == classType) { + return new VScrollTable(); + } else if (VDateFieldCalendar.class == classType) { + return new VDateFieldCalendar(); + } else if (VTextualDate.class == classType) { + return new VTextualDate(); + } else if (VPopupCalendar.class == classType) { + return new VPopupCalendar(); + } else if (VSlider.class == classType) { + return new VSlider(); + } else if (VForm.class == classType) { + return new VForm(); + } else if (VFormLayout.class == classType) { + return new VFormLayout(); + } else if (VUpload.class == classType) { + return new VUpload(); + } else if (VSplitPanelHorizontal.class == classType) { + return new VSplitPanelHorizontal(); + } else if (VSplitPanelVertical.class == classType) { + return new VSplitPanelVertical(); + } else if (VFilterSelect.class == classType) { + return new VFilterSelect(); + } else if (VProgressIndicator.class == classType) { + return new VProgressIndicator(); + } else if (VRichTextArea.class == classType) { + return new VRichTextArea(); + } else if (VAccordion.class == classType) { + return new VAccordion(); + } else if (VMenuBar.class == classType) { + return new VMenuBar(); + } else if (VPopupView.class == classType) { + return new VPopupView(); + } else if (VUriFragmentUtility.class == classType) { + return new VUriFragmentUtility(); + } else if (VAbsoluteLayout.class == classType) { + return new VAbsoluteLayout(); } - return new IUnknownComponent(); + return new VUnknownComponent(); } @@ -152,111 +152,111 @@ public class DefaultWidgetSet implements WidgetSet { final String tag = uidl.getTag(); if ("button".equals(tag)) { if ("switch".equals(uidl.getStringAttribute("type"))) { - return ICheckBox.class; + return VCheckBox.class; } else { - return IButton.class; + return VButton.class; } } else if ("window".equals(tag)) { - return IWindow.class; + return VWindow.class; } else if ("orderedlayout".equals(tag)) { - return IOrderedLayout.class; + return VOrderedLayout.class; } else if ("verticallayout".equals(tag)) { - return IVerticalLayout.class; + return VVerticalLayout.class; } else if ("horizontallayout".equals(tag)) { - return IHorizontalLayout.class; + return VHorizontalLayout.class; } else if ("label".equals(tag)) { - return ILabel.class; + return VLabel.class; } else if ("link".equals(tag)) { - return ILink.class; + return VLink.class; } else if ("gridlayout".equals(tag)) { - return IGridLayout.class; + return VGridLayout.class; } else if ("tree".equals(tag)) { - return ITree.class; + return VTree.class; } else if ("select".equals(tag)) { if (uidl.hasAttribute("type")) { final String type = uidl.getStringAttribute("type"); if (type.equals("twincol")) { - return ITwinColSelect.class; + return VTwinColSelect.class; } if (type.equals("optiongroup")) { - return IOptionGroup.class; + return VOptionGroup.class; } if (type.equals("native")) { - return INativeSelect.class; + return VNativeSelect.class; } if (type.equals("list")) { - return IListSelect.class; + return VListSelect.class; } } else { if (uidl.hasAttribute("selectmode") && uidl.getStringAttribute("selectmode") .equals("multi")) { - return IListSelect.class; + return VListSelect.class; } else { - return IFilterSelect.class; + return VFilterSelect.class; } } } else if ("panel".equals(tag)) { - return IPanel.class; + return VPanel.class; } else if ("tabsheet".equals(tag)) { - return ITabsheet.class; + return VTabsheet.class; } else if ("accordion".equals(tag)) { - return IAccordion.class; + return VAccordion.class; } else if ("embedded".equals(tag)) { - return IEmbedded.class; + return VEmbedded.class; } else if ("customlayout".equals(tag)) { - return ICustomLayout.class; + return VCustomLayout.class; } else if ("customcomponent".equals(tag)) { - return ICustomComponent.class; + return VCustomComponent.class; } else if ("textfield".equals(tag)) { if (uidl.getBooleanAttribute("richtext")) { - return IRichTextArea.class; + return VRichTextArea.class; } else if (uidl.hasAttribute("multiline")) { - return ITextArea.class; + return VTextArea.class; } else if (uidl.getBooleanAttribute("secret")) { - return IPasswordField.class; + return VPasswordField.class; } else { - return ITextField.class; + return VTextField.class; } } else if ("table".equals(tag)) { - return IScrollTable.class; + return VScrollTable.class; } else if ("pagingtable".equals(tag)) { - return ITablePaging.class; + return VTablePaging.class; } else if ("datefield".equals(tag)) { if (uidl.hasAttribute("type")) { if ("inline".equals(uidl.getStringAttribute("type"))) { - return IDateFieldCalendar.class; + return VDateFieldCalendar.class; } else if ("popup".equals(uidl.getStringAttribute("type"))) { - return IPopupCalendar.class; + return VPopupCalendar.class; } } // popup calendar is the default - return IPopupCalendar.class; + return VPopupCalendar.class; } else if ("slider".equals(tag)) { - return ISlider.class; + return VSlider.class; } else if ("form".equals(tag)) { - return IForm.class; + return VForm.class; } else if ("formlayout".equals(tag)) { - return IFormLayout.class; + return VFormLayout.class; } else if ("upload".equals(tag)) { - return IUpload.class; + return VUpload.class; } else if ("hsplitpanel".equals(tag)) { - return ISplitPanelHorizontal.class; + return VSplitPanelHorizontal.class; } else if ("vsplitpanel".equals(tag)) { - return ISplitPanelVertical.class; + return VSplitPanelVertical.class; } else if ("progressindicator".equals(tag)) { - return IProgressIndicator.class; + return VProgressIndicator.class; } else if ("menubar".equals(tag)) { - return IMenuBar.class; + return VMenuBar.class; } else if ("popupview".equals(tag)) { - return IPopupView.class; + return VPopupView.class; } else if ("urifragment".equals(tag)) { - return IUriFragmentUtility.class; - } else if (IAbsoluteLayout.TAGNAME.equals(tag)) { - return IAbsoluteLayout.class; + return VUriFragmentUtility.class; + } else if (VAbsoluteLayout.TAGNAME.equals(tag)) { + return VAbsoluteLayout.class; } - return IUnknownComponent.class; + return VUnknownComponent.class; } /** diff --git a/src/com/vaadin/terminal/gwt/client/ICaption.java b/src/com/vaadin/terminal/gwt/client/ICaption.java deleted file mode 100644 index 972a844bf9..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ICaption.java +++ /dev/null @@ -1,443 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.HTML; -import com.vaadin.terminal.gwt.client.ui.Icon; - -public class ICaption extends HTML { - - public static final String CLASSNAME = "i-caption"; - - private final Paintable owner; - - private Element errorIndicatorElement; - - private Element requiredFieldIndicator; - - private Icon icon; - - private Element captionText; - - private Element clearElement; - - private final ApplicationConnection client; - - private boolean placedAfterComponent = false; - private boolean iconOnloadHandled = false; - - private int maxWidth = -1; - - private static String ATTRIBUTE_ICON = "icon"; - private static String ATTRIBUTE_CAPTION = "caption"; - private static String ATTRIBUTE_DESCRIPTION = "description"; - private static String ATTRIBUTE_REQUIRED = "required"; - private static String ATTRIBUTE_ERROR = "error"; - private static String ATTRIBUTE_HIDEERRORS = "hideErrors"; - - /** - * - * @param component - * optional owner of caption. If not set, getOwner will return - * null - * @param client - */ - public ICaption(Paintable component, ApplicationConnection client) { - super(); - this.client = client; - owner = component; - setStyleName(CLASSNAME); - sinkEvents(ITooltip.TOOLTIP_EVENTS); - - } - - /** - * Updates the caption from UIDL. - * - * @param uidl - * @return true if the position where the caption should be placed has - * changed - */ - public boolean updateCaption(UIDL uidl) { - setVisible(!uidl.getBooleanAttribute("invisible")); - - boolean wasPlacedAfterComponent = placedAfterComponent; - - placedAfterComponent = true; - - String style = CLASSNAME; - if (uidl.hasAttribute("style")) { - final String[] styles = uidl.getStringAttribute("style").split(" "); - for (int i = 0; i < styles.length; i++) { - style += " " + CLASSNAME + "-" + styles[i]; - } - } - - if (uidl.hasAttribute("disabled")) { - style += " " + "i-disabled"; - } - - setStyleName(style); - - if (uidl.hasAttribute(ATTRIBUTE_ICON)) { - if (icon == null) { - icon = new Icon(client); - icon.setWidth("0px"); - icon.setHeight("0px"); - - DOM.insertChild(getElement(), icon.getElement(), - getInsertPosition(ATTRIBUTE_ICON)); - } - placedAfterComponent = false; - - iconOnloadHandled = false; - icon.setUri(uidl.getStringAttribute(ATTRIBUTE_ICON)); - - } else if (icon != null) { - // Remove existing - DOM.removeChild(getElement(), icon.getElement()); - icon = null; - } - - if (uidl.hasAttribute(ATTRIBUTE_CAPTION)) { - if (captionText == null) { - captionText = DOM.createDiv(); - captionText.setClassName("i-captiontext"); - - DOM.insertChild(getElement(), captionText, - getInsertPosition(ATTRIBUTE_CAPTION)); - } - - // Update caption text - String c = uidl.getStringAttribute(ATTRIBUTE_CAPTION); - if (c == null) { - c = ""; - } else { - placedAfterComponent = false; - } - DOM.setInnerText(captionText, c); - } else if (captionText != null) { - // Remove existing - DOM.removeChild(getElement(), captionText); - captionText = null; - } - - if (uidl.hasAttribute(ATTRIBUTE_DESCRIPTION)) { - if (captionText != null) { - addStyleDependentName("hasdescription"); - } else { - removeStyleDependentName("hasdescription"); - } - } - - if (uidl.getBooleanAttribute(ATTRIBUTE_REQUIRED)) { - if (requiredFieldIndicator == null) { - requiredFieldIndicator = DOM.createDiv(); - requiredFieldIndicator - .setClassName("i-required-field-indicator"); - DOM.setInnerText(requiredFieldIndicator, "*"); - - DOM.insertChild(getElement(), requiredFieldIndicator, - getInsertPosition(ATTRIBUTE_REQUIRED)); - } - } else if (requiredFieldIndicator != null) { - // Remove existing - DOM.removeChild(getElement(), requiredFieldIndicator); - requiredFieldIndicator = null; - } - - if (uidl.hasAttribute(ATTRIBUTE_ERROR) - && !uidl.getBooleanAttribute(ATTRIBUTE_HIDEERRORS)) { - if (errorIndicatorElement == null) { - errorIndicatorElement = DOM.createDiv(); - DOM.setInnerHTML(errorIndicatorElement, " "); - DOM.setElementProperty(errorIndicatorElement, "className", - "i-errorindicator"); - - DOM.insertChild(getElement(), errorIndicatorElement, - getInsertPosition(ATTRIBUTE_ERROR)); - } - } else if (errorIndicatorElement != null) { - // Remove existing - DOM.removeChild(getElement(), errorIndicatorElement); - errorIndicatorElement = null; - } - - if (clearElement == null) { - clearElement = DOM.createDiv(); - DOM.setStyleAttribute(clearElement, "clear", "both"); - DOM.setStyleAttribute(clearElement, "width", "0px"); - DOM.setStyleAttribute(clearElement, "height", "0px"); - DOM.setStyleAttribute(clearElement, "overflow", "hidden"); - DOM.appendChild(getElement(), clearElement); - } - - return (wasPlacedAfterComponent != placedAfterComponent); - } - - private int getInsertPosition(String element) { - int pos = 0; - if (element.equals(ATTRIBUTE_ICON)) { - return pos; - } - if (icon != null) { - pos++; - } - - if (element.equals(ATTRIBUTE_CAPTION)) { - return pos; - } - - if (captionText != null) { - pos++; - } - - if (element.equals(ATTRIBUTE_REQUIRED)) { - return pos; - } - if (requiredFieldIndicator != null) { - pos++; - } - - // if (element.equals(ATTRIBUTE_ERROR)) { - // } - return pos; - - } - - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - final Element target = DOM.eventGetTarget(event); - if (client != null && owner != null && target != getElement()) { - client.handleTooltipEvent(event, owner); - } - - if (DOM.eventGetType(event) == Event.ONLOAD - && icon.getElement() == target && !iconOnloadHandled) { - icon.setWidth(""); - icon.setHeight(""); - - /* - * IE6 pngFix causes two onload events to be fired and we want to - * react only to the first one - */ - iconOnloadHandled = true; - - // if max width defined, recalculate - if (maxWidth != -1) { - setMaxWidth(maxWidth); - } else { - String width = getElement().getStyle().getProperty("width"); - if (width != null && !width.equals("")) { - setWidth(getRequiredWidth() + "px"); - } - } - - /* - * The size of the icon might affect the size of the component so we - * must report the size change to the parent TODO consider moving - * the responsibility of reacting to ONLOAD from ICaption to layouts - */ - if (owner != null) { - Util.notifyParentOfSizeChange(owner, true); - } else { - ApplicationConnection - .getConsole() - .log( - "Warning: Icon load event was not propagated because ICaption owner is unknown."); - } - } - } - - public static boolean isNeeded(UIDL uidl) { - if (uidl.getStringAttribute(ATTRIBUTE_CAPTION) != null) { - return true; - } - if (uidl.hasAttribute(ATTRIBUTE_ERROR)) { - return true; - } - if (uidl.hasAttribute(ATTRIBUTE_ICON)) { - return true; - } - if (uidl.hasAttribute(ATTRIBUTE_REQUIRED)) { - return true; - } - - return false; - } - - /** - * Returns Paintable for which this Caption belongs to. - * - * @return owner Widget - */ - public Paintable getOwner() { - return owner; - } - - public boolean shouldBePlacedAfterComponent() { - return placedAfterComponent; - } - - public int getRenderedWidth() { - int width = 0; - - if (icon != null) { - width += Util.getRequiredWidth(icon.getElement()); - } - - if (captionText != null) { - width += Util.getRequiredWidth(captionText); - } - if (requiredFieldIndicator != null) { - width += Util.getRequiredWidth(requiredFieldIndicator); - } - if (errorIndicatorElement != null) { - width += Util.getRequiredWidth(errorIndicatorElement); - } - - return width; - - } - - public int getRequiredWidth() { - int width = 0; - - if (icon != null) { - width += Util.getRequiredWidth(icon.getElement()); - } - if (captionText != null) { - int textWidth = captionText.getScrollWidth(); - if (BrowserInfo.get().isFF3()) { - /* - * In Firefox3 the caption might require more space than the - * scrollWidth returns as scrollWidth is rounded down. - */ - int requiredWidth = Util.getRequiredWidth(captionText); - if (requiredWidth > textWidth) { - textWidth = requiredWidth; - } - - } - width += textWidth; - } - if (requiredFieldIndicator != null) { - width += Util.getRequiredWidth(requiredFieldIndicator); - } - if (errorIndicatorElement != null) { - width += Util.getRequiredWidth(errorIndicatorElement); - } - - return width; - - } - - public int getHeight() { - int height = 0; - int h; - - if (icon != null) { - h = icon.getOffsetHeight(); - if (h > height) { - height = h; - } - } - - if (captionText != null) { - h = captionText.getOffsetHeight(); - if (h > height) { - height = h; - } - } - if (requiredFieldIndicator != null) { - h = requiredFieldIndicator.getOffsetHeight(); - if (h > height) { - height = h; - } - } - if (errorIndicatorElement != null) { - h = errorIndicatorElement.getOffsetHeight(); - if (h > height) { - height = h; - } - } - - return height; - } - - public void setAlignment(String alignment) { - DOM.setStyleAttribute(getElement(), "textAlign", alignment); - } - - public void setMaxWidth(int maxWidth) { - this.maxWidth = maxWidth; - DOM.setStyleAttribute(getElement(), "width", maxWidth + "px"); - - if (icon != null) { - DOM.setStyleAttribute(icon.getElement(), "width", ""); - } - - if (captionText != null) { - DOM.setStyleAttribute(captionText, "width", ""); - } - - int requiredWidth = getRequiredWidth(); - /* - * ApplicationConnection.getConsole().log( "Caption maxWidth: " + - * maxWidth + ", requiredWidth: " + requiredWidth); - */ - if (requiredWidth > maxWidth) { - // Needs to truncate and clip - int availableWidth = maxWidth; - - // DOM.setStyleAttribute(getElement(), "width", maxWidth + "px"); - if (requiredFieldIndicator != null) { - availableWidth -= Util.getRequiredWidth(requiredFieldIndicator); - } - - if (errorIndicatorElement != null) { - availableWidth -= Util.getRequiredWidth(errorIndicatorElement); - } - - if (availableWidth < 0) { - availableWidth = 0; - } - - if (icon != null) { - int iconRequiredWidth = Util - .getRequiredWidth(icon.getElement()); - if (availableWidth > iconRequiredWidth) { - availableWidth -= iconRequiredWidth; - } else { - DOM.setStyleAttribute(icon.getElement(), "width", - availableWidth + "px"); - availableWidth = 0; - } - } - if (captionText != null) { - int captionWidth = Util.getRequiredWidth(captionText); - if (availableWidth > captionWidth) { - availableWidth -= captionWidth; - - } else { - DOM.setStyleAttribute(captionText, "width", availableWidth - + "px"); - availableWidth = 0; - } - - } - - } - } - - protected Element getTextElement() { - return captionText; - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ICaptionWrapper.java b/src/com/vaadin/terminal/gwt/client/ICaptionWrapper.java deleted file mode 100644 index 15f1c5e820..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ICaptionWrapper.java +++ /dev/null @@ -1,32 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client; - -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.Widget; - -public class ICaptionWrapper extends FlowPanel { - - public static final String CLASSNAME = "i-captionwrapper"; - ICaption caption; - Paintable widget; - - public ICaptionWrapper(Paintable toBeWrapped, ApplicationConnection client) { - caption = new ICaption(toBeWrapped, client); - add(caption); - widget = toBeWrapped; - add((Widget) widget); - setStyleName(CLASSNAME); - } - - public void updateCaption(UIDL uidl) { - caption.updateCaption(uidl); - setVisible(!uidl.getBooleanAttribute("invisible")); - } - - public Paintable getPaintable() { - return widget; - } -} diff --git a/src/com/vaadin/terminal/gwt/client/IDebugConsole.java b/src/com/vaadin/terminal/gwt/client/IDebugConsole.java deleted file mode 100755 index 6986aea2ed..0000000000 --- a/src/com/vaadin/terminal/gwt/client/IDebugConsole.java +++ /dev/null @@ -1,466 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client; - -import java.util.List; -import java.util.Set; - -import com.google.gwt.json.client.JSONArray; -import com.google.gwt.json.client.JSONObject; -import com.google.gwt.json.client.JSONValue; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.EventPreview; -import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.Window.Location; -import com.google.gwt.user.client.ui.Button; -import com.google.gwt.user.client.ui.CheckBox; -import com.google.gwt.user.client.ui.ClickListener; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.HorizontalPanel; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.Panel; -import com.google.gwt.user.client.ui.Tree; -import com.google.gwt.user.client.ui.TreeItem; -import com.google.gwt.user.client.ui.VerticalPanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ui.IToolkitOverlay; - -public final class IDebugConsole extends IToolkitOverlay implements Console { - - /** - * Builds number. For example 0-custom_tag in 5.0.0-custom_tag. - */ - public static final String VERSION; - - /* Initialize version numbers from string replaced by build-script. */ - static { - if ("@VERSION@".equals("@" + "VERSION" + "@")) { - VERSION = "5.9.9-INTERNAL-NONVERSIONED-DEBUG-BUILD"; - } else { - VERSION = "@VERSION@"; - } - } - - Element caption = DOM.createDiv(); - - private Panel panel; - - private Button clear = new Button("Clear console"); - private Button restart = new Button("Restart app"); - private Button forceLayout = new Button("Force layout"); - private Button analyzeLayout = new Button("Analyze layouts"); - private HorizontalPanel actions; - private boolean collapsed = false; - - private boolean resizing; - private int startX; - private int startY; - private int initialW; - private int initialH; - - private boolean moving = false; - - private int origTop; - - private int origLeft; - - private ApplicationConnection client; - - private static final String help = "Drag=move, shift-drag=resize, doubleclick=min/max." - + "Use debug=quiet to log only to browser console."; - - public IDebugConsole(ApplicationConnection client, - ApplicationConfiguration cnf, boolean showWindow) { - super(false, false); - - this.client = client; - - panel = new FlowPanel(); - if (showWindow) { - DOM.appendChild(getContainerElement(), caption); - setWidget(panel); - caption.setClassName("i-debug-console-caption"); - setStyleName("i-debug-console"); - DOM.setStyleAttribute(getElement(), "zIndex", 20000 + ""); - DOM.setStyleAttribute(getElement(), "overflow", "hidden"); - - sinkEvents(Event.ONDBLCLICK); - - sinkEvents(Event.MOUSEEVENTS); - - panel.setStyleName("i-debug-console-content"); - - caption.setInnerHTML("Debug window"); - caption.setTitle(help); - - show(); - minimize(); - - actions = new HorizontalPanel(); - actions.add(clear); - actions.add(restart); - actions.add(forceLayout); - actions.add(analyzeLayout); - - panel.add(actions); - - panel.add(new HTML("" + help + "")); - - clear.addClickListener(new ClickListener() { - public void onClick(Widget sender) { - int width = panel.getOffsetWidth(); - int height = panel.getOffsetHeight(); - panel = new FlowPanel(); - panel.setPixelSize(width, height); - panel.setStyleName("i-debug-console-content"); - panel.add(actions); - setWidget(panel); - } - }); - - restart.addClickListener(new ClickListener() { - public void onClick(Widget sender) { - - String queryString = Window.Location.getQueryString(); - if (queryString != null - && queryString.contains("restartApplications")) { - Window.Location.reload(); - } else { - String url = Location.getHref(); - String separator = "?"; - if (url.contains("?")) { - separator = "&"; - } - if (!url.contains("restartApplication")) { - url += separator; - url += "restartApplication"; - } - if (!"".equals(Location.getHash())) { - String hash = Location.getHash(); - url = url.replace(hash, "") + hash; - } - Window.Location.replace(url); - } - - } - }); - - forceLayout.addClickListener(new ClickListener() { - public void onClick(Widget sender) { - IDebugConsole.this.client.forceLayout(); - } - }); - - analyzeLayout.addClickListener(new ClickListener() { - public void onClick(Widget sender) { - List runningApplications = ApplicationConfiguration - .getRunningApplications(); - for (ApplicationConnection applicationConnection : runningApplications) { - applicationConnection.analyzeLayouts(); - } - } - }); - analyzeLayout - .setTitle("Analyzes currently rendered view and " - + "reports possible common problems in usage of relative sizes." - + "Will cause server visit/rendering of whole screen + lose of" - + " all non committed variables form client side."); - - } - - log("Toolkit application servlet version: " + cnf.getServletVersion()); - log("Widget set is built on version: " + VERSION); - log("Application version: " + cnf.getApplicationVersion()); - - if (!cnf.getServletVersion().equals(VERSION)) { - error("Warning: your widget set seems to be built with a different " - + "version than the one used on server. Unexpected " - + "behavior may occur."); - } - } - - private EventPreview dragpreview = new EventPreview() { - - public boolean onEventPreview(Event event) { - onBrowserEvent(event); - return false; - } - }; - - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - switch (DOM.eventGetType(event)) { - case Event.ONMOUSEDOWN: - if (DOM.eventGetShiftKey(event)) { - resizing = true; - DOM.setCapture(getElement()); - startX = DOM.eventGetScreenX(event); - startY = DOM.eventGetScreenY(event); - initialW = IDebugConsole.this.getOffsetWidth(); - initialH = IDebugConsole.this.getOffsetHeight(); - DOM.eventCancelBubble(event, true); - DOM.eventPreventDefault(event); - DOM.addEventPreview(dragpreview); - } else if (DOM.eventGetTarget(event) == caption) { - moving = true; - startX = DOM.eventGetScreenX(event); - startY = DOM.eventGetScreenY(event); - origTop = getAbsoluteTop(); - origLeft = getAbsoluteLeft(); - DOM.eventCancelBubble(event, true); - DOM.eventPreventDefault(event); - DOM.addEventPreview(dragpreview); - } - - break; - case Event.ONMOUSEMOVE: - if (resizing) { - int deltaX = startX - DOM.eventGetScreenX(event); - int detalY = startY - DOM.eventGetScreenY(event); - int w = initialW - deltaX; - if (w < 30) { - w = 30; - } - int h = initialH - detalY; - if (h < 40) { - h = 40; - } - IDebugConsole.this.setPixelSize(w, h); - DOM.eventCancelBubble(event, true); - DOM.eventPreventDefault(event); - } else if (moving) { - int deltaX = startX - DOM.eventGetScreenX(event); - int detalY = startY - DOM.eventGetScreenY(event); - int left = origLeft - deltaX; - if (left < 0) { - left = 0; - } - int top = origTop - detalY; - if (top < 0) { - top = 0; - } - IDebugConsole.this.setPopupPosition(left, top); - DOM.eventCancelBubble(event, true); - DOM.eventPreventDefault(event); - } - break; - case Event.ONLOSECAPTURE: - case Event.ONMOUSEUP: - if (resizing) { - DOM.releaseCapture(getElement()); - resizing = false; - } else if (moving) { - DOM.releaseCapture(getElement()); - moving = false; - } - DOM.removeEventPreview(dragpreview); - break; - case Event.ONDBLCLICK: - if (DOM.eventGetTarget(event) == caption) { - if (collapsed) { - panel.setVisible(true); - setPixelSize(220, 300); - } else { - panel.setVisible(false); - setPixelSize(120, 20); - } - collapsed = !collapsed; - } - break; - default: - break; - } - - } - - private void minimize() { - setPixelSize(400, 150); - setPopupPosition(Window.getClientWidth() - 410, Window - .getClientHeight() - 160); - } - - @Override - public void setPixelSize(int width, int height) { - panel.setHeight((height - 20) + "px"); - panel.setWidth((width - 2) + "px"); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.terminal.gwt.client.Console#log(java.lang.String) - */ - public void log(String msg) { - panel.add(new HTML(msg)); - System.out.println(msg); - consoleLog(msg); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.terminal.gwt.client.Console#error(java.lang.String) - */ - public void error(String msg) { - panel.add((new HTML(msg))); - System.err.println(msg); - consoleErr(msg); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.terminal.gwt.client.Console#printObject(java.lang. - * Object) - */ - public void printObject(Object msg) { - panel.add((new Label(msg.toString()))); - consoleLog(msg.toString()); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.terminal.gwt.client.Console#dirUIDL(com.vaadin - * .terminal.gwt.client.UIDL) - */ - public void dirUIDL(UIDL u) { - panel.add(u.print_r()); - consoleLog(u.getChildrenAsXML()); - } - - private static native void consoleLog(String msg) - /*-{ - if($wnd.console && $wnd.console.log) { - $wnd.console.log(msg); - } - }-*/; - - private static native void consoleErr(String msg) - /*-{ - if($wnd.console) { - if ($wnd.console.error) - $wnd.console.error(msg); - else if ($wnd.console.log) - $wnd.console.log(msg); - } - }-*/; - - public void printLayoutProblems(JSONArray array, ApplicationConnection ac, - Set zeroHeightComponents, - Set zeroWidthComponents) { - int size = array.size(); - panel.add(new HTML("
************************" - + "

Layouts analyzed on server, total top level problems: " - + size + "

")); - if (size > 0) { - Tree tree = new Tree(); - TreeItem root = new TreeItem("Root problems"); - for (int i = 0; i < size; i++) { - JSONObject error = array.get(i).isObject(); - printLayoutError(error, root, ac); - } - panel.add(tree); - tree.addItem(root); - - } - if (zeroHeightComponents.size() > 0 || zeroWidthComponents.size() > 0) { - panel.add(new HTML("

Client side notifications

" - + " Following relative sized components where " - + "rendered to zero size container on client side." - + " Note that these are not necessary invalid " - + "states. Just reported here as they might be.")); - if (zeroHeightComponents.size() > 0) { - panel.add(new HTML( - "

Vertically zero size:

")); - printClientSideDetectedIssues(zeroHeightComponents, ac); - } - if (zeroWidthComponents.size() > 0) { - panel.add(new HTML( - "

Horizontally zero size:

")); - printClientSideDetectedIssues(zeroWidthComponents, ac); - } - } - log("************************"); - } - - private void printClientSideDetectedIssues( - Set zeroHeightComponents, ApplicationConnection ac) { - for (final Paintable paintable : zeroHeightComponents) { - final Container layout = Util.getLayout((Widget) paintable); - - VerticalPanel errorDetails = new VerticalPanel(); - errorDetails.add(new Label("" + Util.getSimpleName(paintable) - + " inside " + Util.getSimpleName(layout))); - final CheckBox emphasisInUi = new CheckBox( - "Emphasis components parent in UI (actual component is not visible)"); - emphasisInUi.addClickListener(new ClickListener() { - public void onClick(Widget sender) { - if (paintable != null) { - Element element2 = ((Widget) layout).getElement(); - Widget.setStyleName(element2, "invalidlayout", - emphasisInUi.isChecked()); - } - } - }); - errorDetails.add(emphasisInUi); - panel.add(errorDetails); - } - } - - private void printLayoutError(JSONObject error, TreeItem parent, - final ApplicationConnection ac) { - final String pid = error.get("id").isString().stringValue(); - final Paintable paintable = ac.getPaintable(pid); - - TreeItem errorNode = new TreeItem(); - VerticalPanel errorDetails = new VerticalPanel(); - errorDetails.add(new Label(Util.getSimpleName(paintable) + " id: " - + pid)); - if (error.containsKey("heightMsg")) { - errorDetails.add(new Label("Height problem: " - + error.get("heightMsg"))); - } - if (error.containsKey("widthMsg")) { - errorDetails.add(new Label("Width problem: " - + error.get("widthMsg"))); - } - final CheckBox emphasisInUi = new CheckBox("Emphasis component in UI"); - emphasisInUi.addClickListener(new ClickListener() { - public void onClick(Widget sender) { - if (paintable != null) { - Element element2 = ((Widget) paintable).getElement(); - Widget.setStyleName(element2, "invalidlayout", emphasisInUi - .isChecked()); - } - } - }); - errorDetails.add(emphasisInUi); - errorNode.setWidget(errorDetails); - if (error.containsKey("subErrors")) { - HTML l = new HTML( - "Expand this node to show problems that may be dependent on this problem."); - errorDetails.add(l); - JSONArray array = error.get("subErrors").isArray(); - for (int i = 0; i < array.size(); i++) { - JSONValue value = array.get(i); - if (value != null && value.isObject() != null) { - printLayoutError(value.isObject(), errorNode, ac); - } else { - System.out.print(value); - } - } - - } - parent.addItem(errorNode); - } -} diff --git a/src/com/vaadin/terminal/gwt/client/IErrorMessage.java b/src/com/vaadin/terminal/gwt/client/IErrorMessage.java deleted file mode 100644 index 0f5f45663e..0000000000 --- a/src/com/vaadin/terminal/gwt/client/IErrorMessage.java +++ /dev/null @@ -1,73 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client; - -import java.util.Iterator; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.HTML; -import com.vaadin.terminal.gwt.client.ui.IToolkitOverlay; - -public class IErrorMessage extends FlowPanel { - public static final String CLASSNAME = "i-errormessage"; - - public IErrorMessage() { - super(); - setStyleName(CLASSNAME); - } - - public void updateFromUIDL(UIDL uidl) { - clear(); - if (uidl.getChildCount() == 0) { - add(new HTML(" ")); - } else { - for (final Iterator it = uidl.getChildIterator(); it.hasNext();) { - final Object child = it.next(); - if (child instanceof String) { - final String errorMessage = (String) child; - add(new HTML(errorMessage)); - } else if (child instanceof UIDL.XML) { - final UIDL.XML xml = (UIDL.XML) child; - add(new HTML(xml.getXMLAsString())); - } else { - final IErrorMessage childError = new IErrorMessage(); - add(childError); - childError.updateFromUIDL((UIDL) child); - } - } - } - } - - /** - * Shows this error message next to given element. - * - * @param indicatorElement - */ - public void showAt(Element indicatorElement) { - IToolkitOverlay errorContainer = (IToolkitOverlay) getParent(); - if (errorContainer == null) { - errorContainer = new IToolkitOverlay(); - errorContainer.setWidget(this); - } - errorContainer.setPopupPosition(DOM.getAbsoluteLeft(indicatorElement) - + 2 - * DOM.getElementPropertyInt(indicatorElement, "offsetHeight"), - DOM.getAbsoluteTop(indicatorElement) - + 2 - * DOM.getElementPropertyInt(indicatorElement, - "offsetHeight")); - errorContainer.show(); - - } - - public void hide() { - final IToolkitOverlay errorContainer = (IToolkitOverlay) getParent(); - if (errorContainer != null) { - errorContainer.hide(); - } - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ITooltip.java b/src/com/vaadin/terminal/gwt/client/ITooltip.java deleted file mode 100644 index bbea0b4bae..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ITooltip.java +++ /dev/null @@ -1,225 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ -package com.vaadin.terminal.gwt.client; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.ui.FlowPanel; -import com.vaadin.terminal.gwt.client.ui.IToolkitOverlay; - -/** - * TODO open for extension - */ -public class ITooltip extends IToolkitOverlay { - private static final String CLASSNAME = "i-tooltip"; - private static final int MARGIN = 4; - public static final int TOOLTIP_EVENTS = Event.ONKEYDOWN - | Event.ONMOUSEOVER | Event.ONMOUSEOUT | Event.ONMOUSEMOVE - | Event.ONCLICK; - protected static final int MAX_WIDTH = 500; - private static final int QUICK_OPEN_TIMEOUT = 1000; - private static final int CLOSE_TIMEOUT = 300; - private static final int OPEN_DELAY = 750; - private static final int QUICK_OPEN_DELAY = 100; - IErrorMessage em = new IErrorMessage(); - Element description = DOM.createDiv(); - private Paintable tooltipOwner; - private boolean closing = false; - private boolean opening = false; - private ApplicationConnection ac; - // Open next tooltip faster. Disabled after 2 sec of showTooltip-silence. - private boolean justClosed = false; - - public ITooltip(ApplicationConnection client) { - super(false, false, true); - ac = client; - setStyleName(CLASSNAME); - FlowPanel layout = new FlowPanel(); - setWidget(layout); - layout.add(em); - DOM.setElementProperty(description, "className", CLASSNAME + "-text"); - DOM.appendChild(layout.getElement(), description); - } - - private void show(TooltipInfo info) { - boolean hasContent = false; - if (info.getErrorUidl() != null) { - em.setVisible(true); - em.updateFromUIDL(info.getErrorUidl()); - hasContent = true; - } else { - em.setVisible(false); - } - if (info.getTitle() != null && !"".equals(info.getTitle())) { - DOM.setInnerHTML(description, info.getTitle()); - DOM.setStyleAttribute(description, "display", ""); - hasContent = true; - } else { - DOM.setInnerHTML(description, ""); - DOM.setStyleAttribute(description, "display", "none"); - } - if (hasContent) { - setPopupPositionAndShow(new PositionCallback() { - public void setPosition(int offsetWidth, int offsetHeight) { - - if (offsetWidth > MAX_WIDTH) { - setWidth(MAX_WIDTH + "px"); - } - - offsetWidth = getOffsetWidth(); - - int x = tooltipEventMouseX + 10 + Window.getScrollLeft(); - int y = tooltipEventMouseY + 10 + Window.getScrollTop(); - - if (x + offsetWidth + MARGIN - Window.getScrollLeft() > Window - .getClientWidth()) { - x = Window.getClientWidth() - offsetWidth - MARGIN; - } - - if (y + offsetHeight + MARGIN - Window.getScrollTop() > Window - .getClientHeight()) { - y = tooltipEventMouseY - 5 - offsetHeight; - } - - setPopupPosition(x, y); - sinkEvents(Event.ONMOUSEOVER | Event.ONMOUSEOUT); - } - }); - } else { - hide(); - } - } - - public void showTooltip(Paintable owner, Event event) { - if (closing && tooltipOwner == owner) { - // return to same tooltip, cancel closing - closeTimer.cancel(); - closing = false; - justClosedTimer.cancel(); - justClosed = false; - return; - } - - if (closing) { - closeNow(); - } - - updatePosition(event); - - if (opening) { - showTimer.cancel(); - } - tooltipOwner = owner; - if (justClosed) { - showTimer.schedule(QUICK_OPEN_DELAY); - } else { - showTimer.schedule(OPEN_DELAY); - } - opening = true; - } - - private void closeNow() { - if (closing) { - hide(); - tooltipOwner = null; - setWidth(""); - closing = false; - } - } - - private Timer showTimer = new Timer() { - @Override - public void run() { - TooltipInfo info = ac.getTitleInfo(tooltipOwner); - if (null != info) { - show(info); - } - opening = false; - } - }; - - private Timer closeTimer = new Timer() { - @Override - public void run() { - closeNow(); - justClosedTimer.schedule(2000); - justClosed = true; - } - }; - - private Timer justClosedTimer = new Timer() { - @Override - public void run() { - justClosed = false; - } - }; - - public void hideTooltip() { - if (opening) { - showTimer.cancel(); - opening = false; - tooltipOwner = null; - } - if (!isAttached()) { - return; - } - if (closing) { - // already about to close - return; - } - closeTimer.schedule(CLOSE_TIMEOUT); - closing = true; - justClosed = true; - justClosedTimer.schedule(QUICK_OPEN_TIMEOUT); - - } - - private int tooltipEventMouseX; - private int tooltipEventMouseY; - - public void updatePosition(Event event) { - tooltipEventMouseX = DOM.eventGetClientX(event); - tooltipEventMouseY = DOM.eventGetClientY(event); - - } - - public void handleTooltipEvent(Event event, Paintable owner) { - final int type = DOM.eventGetType(event); - if ((ITooltip.TOOLTIP_EVENTS & type) == type) { - if (type == Event.ONMOUSEOVER) { - showTooltip(owner, event); - } else if (type == Event.ONMOUSEMOVE) { - updatePosition(event); - } else { - hideTooltip(); - } - } else { - // non-tooltip event, hide tooltip - hideTooltip(); - } - } - - @Override - public void onBrowserEvent(Event event) { - final int type = DOM.eventGetType(event); - // cancel closing event if tooltip is mouseovered; the user might want - // to scroll of cut&paste - - switch (type) { - case Event.ONMOUSEOVER: - closeTimer.cancel(); - closing = false; - break; - case Event.ONMOUSEOUT: - hideTooltip(); - break; - default: - // NOP - } - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/UIDL.java b/src/com/vaadin/terminal/gwt/client/UIDL.java index 2205f6b6dd..8429c8a0d9 100644 --- a/src/com/vaadin/terminal/gwt/client/UIDL.java +++ b/src/com/vaadin/terminal/gwt/client/UIDL.java @@ -238,12 +238,12 @@ public class UIDL { return s; } - public IUIDLBrowser print_r() { - return new IUIDLBrowser(); + public VUIDLBrowser print_r() { + return new VUIDLBrowser(); } - private class IUIDLBrowser extends Tree { - public IUIDLBrowser() { + private class VUIDLBrowser extends Tree { + public VUIDLBrowser() { DOM.setStyleAttribute(getElement(), "position", ""); @@ -255,7 +255,7 @@ public class UIDL { public void onTreeItemStateChanged(TreeItem item) { if (item == root) { removeItem(root); - IUIDLBrowser.this.removeTreeListener(this); + VUIDLBrowser.this.removeTreeListener(this); addItem(dir()); final Iterator it = treeItemIterator(); while (it.hasNext()) { diff --git a/src/com/vaadin/terminal/gwt/client/VCaption.java b/src/com/vaadin/terminal/gwt/client/VCaption.java new file mode 100644 index 0000000000..730680647e --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/VCaption.java @@ -0,0 +1,443 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.HTML; +import com.vaadin.terminal.gwt.client.ui.Icon; + +public class VCaption extends HTML { + + public static final String CLASSNAME = "i-caption"; + + private final Paintable owner; + + private Element errorIndicatorElement; + + private Element requiredFieldIndicator; + + private Icon icon; + + private Element captionText; + + private Element clearElement; + + private final ApplicationConnection client; + + private boolean placedAfterComponent = false; + private boolean iconOnloadHandled = false; + + private int maxWidth = -1; + + private static String ATTRIBUTE_ICON = "icon"; + private static String ATTRIBUTE_CAPTION = "caption"; + private static String ATTRIBUTE_DESCRIPTION = "description"; + private static String ATTRIBUTE_REQUIRED = "required"; + private static String ATTRIBUTE_ERROR = "error"; + private static String ATTRIBUTE_HIDEERRORS = "hideErrors"; + + /** + * + * @param component + * optional owner of caption. If not set, getOwner will return + * null + * @param client + */ + public VCaption(Paintable component, ApplicationConnection client) { + super(); + this.client = client; + owner = component; + setStyleName(CLASSNAME); + sinkEvents(VTooltip.TOOLTIP_EVENTS); + + } + + /** + * Updates the caption from UIDL. + * + * @param uidl + * @return true if the position where the caption should be placed has + * changed + */ + public boolean updateCaption(UIDL uidl) { + setVisible(!uidl.getBooleanAttribute("invisible")); + + boolean wasPlacedAfterComponent = placedAfterComponent; + + placedAfterComponent = true; + + String style = CLASSNAME; + if (uidl.hasAttribute("style")) { + final String[] styles = uidl.getStringAttribute("style").split(" "); + for (int i = 0; i < styles.length; i++) { + style += " " + CLASSNAME + "-" + styles[i]; + } + } + + if (uidl.hasAttribute("disabled")) { + style += " " + "i-disabled"; + } + + setStyleName(style); + + if (uidl.hasAttribute(ATTRIBUTE_ICON)) { + if (icon == null) { + icon = new Icon(client); + icon.setWidth("0px"); + icon.setHeight("0px"); + + DOM.insertChild(getElement(), icon.getElement(), + getInsertPosition(ATTRIBUTE_ICON)); + } + placedAfterComponent = false; + + iconOnloadHandled = false; + icon.setUri(uidl.getStringAttribute(ATTRIBUTE_ICON)); + + } else if (icon != null) { + // Remove existing + DOM.removeChild(getElement(), icon.getElement()); + icon = null; + } + + if (uidl.hasAttribute(ATTRIBUTE_CAPTION)) { + if (captionText == null) { + captionText = DOM.createDiv(); + captionText.setClassName("i-captiontext"); + + DOM.insertChild(getElement(), captionText, + getInsertPosition(ATTRIBUTE_CAPTION)); + } + + // Update caption text + String c = uidl.getStringAttribute(ATTRIBUTE_CAPTION); + if (c == null) { + c = ""; + } else { + placedAfterComponent = false; + } + DOM.setInnerText(captionText, c); + } else if (captionText != null) { + // Remove existing + DOM.removeChild(getElement(), captionText); + captionText = null; + } + + if (uidl.hasAttribute(ATTRIBUTE_DESCRIPTION)) { + if (captionText != null) { + addStyleDependentName("hasdescription"); + } else { + removeStyleDependentName("hasdescription"); + } + } + + if (uidl.getBooleanAttribute(ATTRIBUTE_REQUIRED)) { + if (requiredFieldIndicator == null) { + requiredFieldIndicator = DOM.createDiv(); + requiredFieldIndicator + .setClassName("i-required-field-indicator"); + DOM.setInnerText(requiredFieldIndicator, "*"); + + DOM.insertChild(getElement(), requiredFieldIndicator, + getInsertPosition(ATTRIBUTE_REQUIRED)); + } + } else if (requiredFieldIndicator != null) { + // Remove existing + DOM.removeChild(getElement(), requiredFieldIndicator); + requiredFieldIndicator = null; + } + + if (uidl.hasAttribute(ATTRIBUTE_ERROR) + && !uidl.getBooleanAttribute(ATTRIBUTE_HIDEERRORS)) { + if (errorIndicatorElement == null) { + errorIndicatorElement = DOM.createDiv(); + DOM.setInnerHTML(errorIndicatorElement, " "); + DOM.setElementProperty(errorIndicatorElement, "className", + "i-errorindicator"); + + DOM.insertChild(getElement(), errorIndicatorElement, + getInsertPosition(ATTRIBUTE_ERROR)); + } + } else if (errorIndicatorElement != null) { + // Remove existing + DOM.removeChild(getElement(), errorIndicatorElement); + errorIndicatorElement = null; + } + + if (clearElement == null) { + clearElement = DOM.createDiv(); + DOM.setStyleAttribute(clearElement, "clear", "both"); + DOM.setStyleAttribute(clearElement, "width", "0px"); + DOM.setStyleAttribute(clearElement, "height", "0px"); + DOM.setStyleAttribute(clearElement, "overflow", "hidden"); + DOM.appendChild(getElement(), clearElement); + } + + return (wasPlacedAfterComponent != placedAfterComponent); + } + + private int getInsertPosition(String element) { + int pos = 0; + if (element.equals(ATTRIBUTE_ICON)) { + return pos; + } + if (icon != null) { + pos++; + } + + if (element.equals(ATTRIBUTE_CAPTION)) { + return pos; + } + + if (captionText != null) { + pos++; + } + + if (element.equals(ATTRIBUTE_REQUIRED)) { + return pos; + } + if (requiredFieldIndicator != null) { + pos++; + } + + // if (element.equals(ATTRIBUTE_ERROR)) { + // } + return pos; + + } + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + final Element target = DOM.eventGetTarget(event); + if (client != null && owner != null && target != getElement()) { + client.handleTooltipEvent(event, owner); + } + + if (DOM.eventGetType(event) == Event.ONLOAD + && icon.getElement() == target && !iconOnloadHandled) { + icon.setWidth(""); + icon.setHeight(""); + + /* + * IE6 pngFix causes two onload events to be fired and we want to + * react only to the first one + */ + iconOnloadHandled = true; + + // if max width defined, recalculate + if (maxWidth != -1) { + setMaxWidth(maxWidth); + } else { + String width = getElement().getStyle().getProperty("width"); + if (width != null && !width.equals("")) { + setWidth(getRequiredWidth() + "px"); + } + } + + /* + * The size of the icon might affect the size of the component so we + * must report the size change to the parent TODO consider moving + * the responsibility of reacting to ONLOAD from VCaption to layouts + */ + if (owner != null) { + Util.notifyParentOfSizeChange(owner, true); + } else { + ApplicationConnection + .getConsole() + .log( + "Warning: Icon load event was not propagated because VCaption owner is unknown."); + } + } + } + + public static boolean isNeeded(UIDL uidl) { + if (uidl.getStringAttribute(ATTRIBUTE_CAPTION) != null) { + return true; + } + if (uidl.hasAttribute(ATTRIBUTE_ERROR)) { + return true; + } + if (uidl.hasAttribute(ATTRIBUTE_ICON)) { + return true; + } + if (uidl.hasAttribute(ATTRIBUTE_REQUIRED)) { + return true; + } + + return false; + } + + /** + * Returns Paintable for which this Caption belongs to. + * + * @return owner Widget + */ + public Paintable getOwner() { + return owner; + } + + public boolean shouldBePlacedAfterComponent() { + return placedAfterComponent; + } + + public int getRenderedWidth() { + int width = 0; + + if (icon != null) { + width += Util.getRequiredWidth(icon.getElement()); + } + + if (captionText != null) { + width += Util.getRequiredWidth(captionText); + } + if (requiredFieldIndicator != null) { + width += Util.getRequiredWidth(requiredFieldIndicator); + } + if (errorIndicatorElement != null) { + width += Util.getRequiredWidth(errorIndicatorElement); + } + + return width; + + } + + public int getRequiredWidth() { + int width = 0; + + if (icon != null) { + width += Util.getRequiredWidth(icon.getElement()); + } + if (captionText != null) { + int textWidth = captionText.getScrollWidth(); + if (BrowserInfo.get().isFF3()) { + /* + * In Firefox3 the caption might require more space than the + * scrollWidth returns as scrollWidth is rounded down. + */ + int requiredWidth = Util.getRequiredWidth(captionText); + if (requiredWidth > textWidth) { + textWidth = requiredWidth; + } + + } + width += textWidth; + } + if (requiredFieldIndicator != null) { + width += Util.getRequiredWidth(requiredFieldIndicator); + } + if (errorIndicatorElement != null) { + width += Util.getRequiredWidth(errorIndicatorElement); + } + + return width; + + } + + public int getHeight() { + int height = 0; + int h; + + if (icon != null) { + h = icon.getOffsetHeight(); + if (h > height) { + height = h; + } + } + + if (captionText != null) { + h = captionText.getOffsetHeight(); + if (h > height) { + height = h; + } + } + if (requiredFieldIndicator != null) { + h = requiredFieldIndicator.getOffsetHeight(); + if (h > height) { + height = h; + } + } + if (errorIndicatorElement != null) { + h = errorIndicatorElement.getOffsetHeight(); + if (h > height) { + height = h; + } + } + + return height; + } + + public void setAlignment(String alignment) { + DOM.setStyleAttribute(getElement(), "textAlign", alignment); + } + + public void setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + DOM.setStyleAttribute(getElement(), "width", maxWidth + "px"); + + if (icon != null) { + DOM.setStyleAttribute(icon.getElement(), "width", ""); + } + + if (captionText != null) { + DOM.setStyleAttribute(captionText, "width", ""); + } + + int requiredWidth = getRequiredWidth(); + /* + * ApplicationConnection.getConsole().log( "Caption maxWidth: " + + * maxWidth + ", requiredWidth: " + requiredWidth); + */ + if (requiredWidth > maxWidth) { + // Needs to truncate and clip + int availableWidth = maxWidth; + + // DOM.setStyleAttribute(getElement(), "width", maxWidth + "px"); + if (requiredFieldIndicator != null) { + availableWidth -= Util.getRequiredWidth(requiredFieldIndicator); + } + + if (errorIndicatorElement != null) { + availableWidth -= Util.getRequiredWidth(errorIndicatorElement); + } + + if (availableWidth < 0) { + availableWidth = 0; + } + + if (icon != null) { + int iconRequiredWidth = Util + .getRequiredWidth(icon.getElement()); + if (availableWidth > iconRequiredWidth) { + availableWidth -= iconRequiredWidth; + } else { + DOM.setStyleAttribute(icon.getElement(), "width", + availableWidth + "px"); + availableWidth = 0; + } + } + if (captionText != null) { + int captionWidth = Util.getRequiredWidth(captionText); + if (availableWidth > captionWidth) { + availableWidth -= captionWidth; + + } else { + DOM.setStyleAttribute(captionText, "width", availableWidth + + "px"); + availableWidth = 0; + } + + } + + } + } + + protected Element getTextElement() { + return captionText; + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/VCaptionWrapper.java b/src/com/vaadin/terminal/gwt/client/VCaptionWrapper.java new file mode 100644 index 0000000000..f67eb77f25 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/VCaptionWrapper.java @@ -0,0 +1,32 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client; + +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.Widget; + +public class VCaptionWrapper extends FlowPanel { + + public static final String CLASSNAME = "i-captionwrapper"; + VCaption caption; + Paintable widget; + + public VCaptionWrapper(Paintable toBeWrapped, ApplicationConnection client) { + caption = new VCaption(toBeWrapped, client); + add(caption); + widget = toBeWrapped; + add((Widget) widget); + setStyleName(CLASSNAME); + } + + public void updateCaption(UIDL uidl) { + caption.updateCaption(uidl); + setVisible(!uidl.getBooleanAttribute("invisible")); + } + + public Paintable getPaintable() { + return widget; + } +} diff --git a/src/com/vaadin/terminal/gwt/client/VDebugConsole.java b/src/com/vaadin/terminal/gwt/client/VDebugConsole.java new file mode 100755 index 0000000000..aa7663abee --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/VDebugConsole.java @@ -0,0 +1,466 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client; + +import java.util.List; +import java.util.Set; + +import com.google.gwt.json.client.JSONArray; +import com.google.gwt.json.client.JSONObject; +import com.google.gwt.json.client.JSONValue; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.EventPreview; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.Window.Location; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.CheckBox; +import com.google.gwt.user.client.ui.ClickListener; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.Panel; +import com.google.gwt.user.client.ui.Tree; +import com.google.gwt.user.client.ui.TreeItem; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ui.VToolkitOverlay; + +public final class VDebugConsole extends VToolkitOverlay implements Console { + + /** + * Builds number. For example 0-custom_tag in 5.0.0-custom_tag. + */ + public static final String VERSION; + + /* Initialize version numbers from string replaced by build-script. */ + static { + if ("@VERSION@".equals("@" + "VERSION" + "@")) { + VERSION = "5.9.9-INTERNAL-NONVERSIONED-DEBUG-BUILD"; + } else { + VERSION = "@VERSION@"; + } + } + + Element caption = DOM.createDiv(); + + private Panel panel; + + private Button clear = new Button("Clear console"); + private Button restart = new Button("Restart app"); + private Button forceLayout = new Button("Force layout"); + private Button analyzeLayout = new Button("Analyze layouts"); + private HorizontalPanel actions; + private boolean collapsed = false; + + private boolean resizing; + private int startX; + private int startY; + private int initialW; + private int initialH; + + private boolean moving = false; + + private int origTop; + + private int origLeft; + + private ApplicationConnection client; + + private static final String help = "Drag=move, shift-drag=resize, doubleclick=min/max." + + "Use debug=quiet to log only to browser console."; + + public VDebugConsole(ApplicationConnection client, + ApplicationConfiguration cnf, boolean showWindow) { + super(false, false); + + this.client = client; + + panel = new FlowPanel(); + if (showWindow) { + DOM.appendChild(getContainerElement(), caption); + setWidget(panel); + caption.setClassName("i-debug-console-caption"); + setStyleName("i-debug-console"); + DOM.setStyleAttribute(getElement(), "zIndex", 20000 + ""); + DOM.setStyleAttribute(getElement(), "overflow", "hidden"); + + sinkEvents(Event.ONDBLCLICK); + + sinkEvents(Event.MOUSEEVENTS); + + panel.setStyleName("i-debug-console-content"); + + caption.setInnerHTML("Debug window"); + caption.setTitle(help); + + show(); + minimize(); + + actions = new HorizontalPanel(); + actions.add(clear); + actions.add(restart); + actions.add(forceLayout); + actions.add(analyzeLayout); + + panel.add(actions); + + panel.add(new HTML("" + help + "")); + + clear.addClickListener(new ClickListener() { + public void onClick(Widget sender) { + int width = panel.getOffsetWidth(); + int height = panel.getOffsetHeight(); + panel = new FlowPanel(); + panel.setPixelSize(width, height); + panel.setStyleName("i-debug-console-content"); + panel.add(actions); + setWidget(panel); + } + }); + + restart.addClickListener(new ClickListener() { + public void onClick(Widget sender) { + + String queryString = Window.Location.getQueryString(); + if (queryString != null + && queryString.contains("restartApplications")) { + Window.Location.reload(); + } else { + String url = Location.getHref(); + String separator = "?"; + if (url.contains("?")) { + separator = "&"; + } + if (!url.contains("restartApplication")) { + url += separator; + url += "restartApplication"; + } + if (!"".equals(Location.getHash())) { + String hash = Location.getHash(); + url = url.replace(hash, "") + hash; + } + Window.Location.replace(url); + } + + } + }); + + forceLayout.addClickListener(new ClickListener() { + public void onClick(Widget sender) { + VDebugConsole.this.client.forceLayout(); + } + }); + + analyzeLayout.addClickListener(new ClickListener() { + public void onClick(Widget sender) { + List runningApplications = ApplicationConfiguration + .getRunningApplications(); + for (ApplicationConnection applicationConnection : runningApplications) { + applicationConnection.analyzeLayouts(); + } + } + }); + analyzeLayout + .setTitle("Analyzes currently rendered view and " + + "reports possible common problems in usage of relative sizes." + + "Will cause server visit/rendering of whole screen + lose of" + + " all non committed variables form client side."); + + } + + log("Toolkit application servlet version: " + cnf.getServletVersion()); + log("Widget set is built on version: " + VERSION); + log("Application version: " + cnf.getApplicationVersion()); + + if (!cnf.getServletVersion().equals(VERSION)) { + error("Warning: your widget set seems to be built with a different " + + "version than the one used on server. Unexpected " + + "behavior may occur."); + } + } + + private EventPreview dragpreview = new EventPreview() { + + public boolean onEventPreview(Event event) { + onBrowserEvent(event); + return false; + } + }; + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + switch (DOM.eventGetType(event)) { + case Event.ONMOUSEDOWN: + if (DOM.eventGetShiftKey(event)) { + resizing = true; + DOM.setCapture(getElement()); + startX = DOM.eventGetScreenX(event); + startY = DOM.eventGetScreenY(event); + initialW = VDebugConsole.this.getOffsetWidth(); + initialH = VDebugConsole.this.getOffsetHeight(); + DOM.eventCancelBubble(event, true); + DOM.eventPreventDefault(event); + DOM.addEventPreview(dragpreview); + } else if (DOM.eventGetTarget(event) == caption) { + moving = true; + startX = DOM.eventGetScreenX(event); + startY = DOM.eventGetScreenY(event); + origTop = getAbsoluteTop(); + origLeft = getAbsoluteLeft(); + DOM.eventCancelBubble(event, true); + DOM.eventPreventDefault(event); + DOM.addEventPreview(dragpreview); + } + + break; + case Event.ONMOUSEMOVE: + if (resizing) { + int deltaX = startX - DOM.eventGetScreenX(event); + int detalY = startY - DOM.eventGetScreenY(event); + int w = initialW - deltaX; + if (w < 30) { + w = 30; + } + int h = initialH - detalY; + if (h < 40) { + h = 40; + } + VDebugConsole.this.setPixelSize(w, h); + DOM.eventCancelBubble(event, true); + DOM.eventPreventDefault(event); + } else if (moving) { + int deltaX = startX - DOM.eventGetScreenX(event); + int detalY = startY - DOM.eventGetScreenY(event); + int left = origLeft - deltaX; + if (left < 0) { + left = 0; + } + int top = origTop - detalY; + if (top < 0) { + top = 0; + } + VDebugConsole.this.setPopupPosition(left, top); + DOM.eventCancelBubble(event, true); + DOM.eventPreventDefault(event); + } + break; + case Event.ONLOSECAPTURE: + case Event.ONMOUSEUP: + if (resizing) { + DOM.releaseCapture(getElement()); + resizing = false; + } else if (moving) { + DOM.releaseCapture(getElement()); + moving = false; + } + DOM.removeEventPreview(dragpreview); + break; + case Event.ONDBLCLICK: + if (DOM.eventGetTarget(event) == caption) { + if (collapsed) { + panel.setVisible(true); + setPixelSize(220, 300); + } else { + panel.setVisible(false); + setPixelSize(120, 20); + } + collapsed = !collapsed; + } + break; + default: + break; + } + + } + + private void minimize() { + setPixelSize(400, 150); + setPopupPosition(Window.getClientWidth() - 410, Window + .getClientHeight() - 160); + } + + @Override + public void setPixelSize(int width, int height) { + panel.setHeight((height - 20) + "px"); + panel.setWidth((width - 2) + "px"); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.terminal.gwt.client.Console#log(java.lang.String) + */ + public void log(String msg) { + panel.add(new HTML(msg)); + System.out.println(msg); + consoleLog(msg); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.Console#error(java.lang.String) + */ + public void error(String msg) { + panel.add((new HTML(msg))); + System.err.println(msg); + consoleErr(msg); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.Console#printObject(java.lang. + * Object) + */ + public void printObject(Object msg) { + panel.add((new Label(msg.toString()))); + consoleLog(msg.toString()); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.Console#dirUIDL(com.vaadin + * .terminal.gwt.client.UIDL) + */ + public void dirUIDL(UIDL u) { + panel.add(u.print_r()); + consoleLog(u.getChildrenAsXML()); + } + + private static native void consoleLog(String msg) + /*-{ + if($wnd.console && $wnd.console.log) { + $wnd.console.log(msg); + } + }-*/; + + private static native void consoleErr(String msg) + /*-{ + if($wnd.console) { + if ($wnd.console.error) + $wnd.console.error(msg); + else if ($wnd.console.log) + $wnd.console.log(msg); + } + }-*/; + + public void printLayoutProblems(JSONArray array, ApplicationConnection ac, + Set zeroHeightComponents, + Set zeroWidthComponents) { + int size = array.size(); + panel.add(new HTML("

************************" + + "

Layouts analyzed on server, total top level problems: " + + size + "

")); + if (size > 0) { + Tree tree = new Tree(); + TreeItem root = new TreeItem("Root problems"); + for (int i = 0; i < size; i++) { + JSONObject error = array.get(i).isObject(); + printLayoutError(error, root, ac); + } + panel.add(tree); + tree.addItem(root); + + } + if (zeroHeightComponents.size() > 0 || zeroWidthComponents.size() > 0) { + panel.add(new HTML("

Client side notifications

" + + " Following relative sized components where " + + "rendered to zero size container on client side." + + " Note that these are not necessary invalid " + + "states. Just reported here as they might be.")); + if (zeroHeightComponents.size() > 0) { + panel.add(new HTML( + "

Vertically zero size:

")); + printClientSideDetectedIssues(zeroHeightComponents, ac); + } + if (zeroWidthComponents.size() > 0) { + panel.add(new HTML( + "

Horizontally zero size:

")); + printClientSideDetectedIssues(zeroWidthComponents, ac); + } + } + log("************************"); + } + + private void printClientSideDetectedIssues( + Set zeroHeightComponents, ApplicationConnection ac) { + for (final Paintable paintable : zeroHeightComponents) { + final Container layout = Util.getLayout((Widget) paintable); + + VerticalPanel errorDetails = new VerticalPanel(); + errorDetails.add(new Label("" + Util.getSimpleName(paintable) + + " inside " + Util.getSimpleName(layout))); + final CheckBox emphasisInUi = new CheckBox( + "Emphasis components parent in UI (actual component is not visible)"); + emphasisInUi.addClickListener(new ClickListener() { + public void onClick(Widget sender) { + if (paintable != null) { + Element element2 = ((Widget) layout).getElement(); + Widget.setStyleName(element2, "invalidlayout", + emphasisInUi.isChecked()); + } + } + }); + errorDetails.add(emphasisInUi); + panel.add(errorDetails); + } + } + + private void printLayoutError(JSONObject error, TreeItem parent, + final ApplicationConnection ac) { + final String pid = error.get("id").isString().stringValue(); + final Paintable paintable = ac.getPaintable(pid); + + TreeItem errorNode = new TreeItem(); + VerticalPanel errorDetails = new VerticalPanel(); + errorDetails.add(new Label(Util.getSimpleName(paintable) + " id: " + + pid)); + if (error.containsKey("heightMsg")) { + errorDetails.add(new Label("Height problem: " + + error.get("heightMsg"))); + } + if (error.containsKey("widthMsg")) { + errorDetails.add(new Label("Width problem: " + + error.get("widthMsg"))); + } + final CheckBox emphasisInUi = new CheckBox("Emphasis component in UI"); + emphasisInUi.addClickListener(new ClickListener() { + public void onClick(Widget sender) { + if (paintable != null) { + Element element2 = ((Widget) paintable).getElement(); + Widget.setStyleName(element2, "invalidlayout", emphasisInUi + .isChecked()); + } + } + }); + errorDetails.add(emphasisInUi); + errorNode.setWidget(errorDetails); + if (error.containsKey("subErrors")) { + HTML l = new HTML( + "Expand this node to show problems that may be dependent on this problem."); + errorDetails.add(l); + JSONArray array = error.get("subErrors").isArray(); + for (int i = 0; i < array.size(); i++) { + JSONValue value = array.get(i); + if (value != null && value.isObject() != null) { + printLayoutError(value.isObject(), errorNode, ac); + } else { + System.out.print(value); + } + } + + } + parent.addItem(errorNode); + } +} diff --git a/src/com/vaadin/terminal/gwt/client/VErrorMessage.java b/src/com/vaadin/terminal/gwt/client/VErrorMessage.java new file mode 100644 index 0000000000..81817405a2 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/VErrorMessage.java @@ -0,0 +1,73 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client; + +import java.util.Iterator; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.HTML; +import com.vaadin.terminal.gwt.client.ui.VToolkitOverlay; + +public class VErrorMessage extends FlowPanel { + public static final String CLASSNAME = "i-errormessage"; + + public VErrorMessage() { + super(); + setStyleName(CLASSNAME); + } + + public void updateFromUIDL(UIDL uidl) { + clear(); + if (uidl.getChildCount() == 0) { + add(new HTML(" ")); + } else { + for (final Iterator it = uidl.getChildIterator(); it.hasNext();) { + final Object child = it.next(); + if (child instanceof String) { + final String errorMessage = (String) child; + add(new HTML(errorMessage)); + } else if (child instanceof UIDL.XML) { + final UIDL.XML xml = (UIDL.XML) child; + add(new HTML(xml.getXMLAsString())); + } else { + final VErrorMessage childError = new VErrorMessage(); + add(childError); + childError.updateFromUIDL((UIDL) child); + } + } + } + } + + /** + * Shows this error message next to given element. + * + * @param indicatorElement + */ + public void showAt(Element indicatorElement) { + VToolkitOverlay errorContainer = (VToolkitOverlay) getParent(); + if (errorContainer == null) { + errorContainer = new VToolkitOverlay(); + errorContainer.setWidget(this); + } + errorContainer.setPopupPosition(DOM.getAbsoluteLeft(indicatorElement) + + 2 + * DOM.getElementPropertyInt(indicatorElement, "offsetHeight"), + DOM.getAbsoluteTop(indicatorElement) + + 2 + * DOM.getElementPropertyInt(indicatorElement, + "offsetHeight")); + errorContainer.show(); + + } + + public void hide() { + final VToolkitOverlay errorContainer = (VToolkitOverlay) getParent(); + if (errorContainer != null) { + errorContainer.hide(); + } + } +} diff --git a/src/com/vaadin/terminal/gwt/client/VTooltip.java b/src/com/vaadin/terminal/gwt/client/VTooltip.java new file mode 100644 index 0000000000..9e02dc4cc5 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/VTooltip.java @@ -0,0 +1,225 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.client; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.ui.FlowPanel; +import com.vaadin.terminal.gwt.client.ui.VToolkitOverlay; + +/** + * TODO open for extension + */ +public class VTooltip extends VToolkitOverlay { + private static final String CLASSNAME = "i-tooltip"; + private static final int MARGIN = 4; + public static final int TOOLTIP_EVENTS = Event.ONKEYDOWN + | Event.ONMOUSEOVER | Event.ONMOUSEOUT | Event.ONMOUSEMOVE + | Event.ONCLICK; + protected static final int MAX_WIDTH = 500; + private static final int QUICK_OPEN_TIMEOUT = 1000; + private static final int CLOSE_TIMEOUT = 300; + private static final int OPEN_DELAY = 750; + private static final int QUICK_OPEN_DELAY = 100; + VErrorMessage em = new VErrorMessage(); + Element description = DOM.createDiv(); + private Paintable tooltipOwner; + private boolean closing = false; + private boolean opening = false; + private ApplicationConnection ac; + // Open next tooltip faster. Disabled after 2 sec of showTooltip-silence. + private boolean justClosed = false; + + public VTooltip(ApplicationConnection client) { + super(false, false, true); + ac = client; + setStyleName(CLASSNAME); + FlowPanel layout = new FlowPanel(); + setWidget(layout); + layout.add(em); + DOM.setElementProperty(description, "className", CLASSNAME + "-text"); + DOM.appendChild(layout.getElement(), description); + } + + private void show(TooltipInfo info) { + boolean hasContent = false; + if (info.getErrorUidl() != null) { + em.setVisible(true); + em.updateFromUIDL(info.getErrorUidl()); + hasContent = true; + } else { + em.setVisible(false); + } + if (info.getTitle() != null && !"".equals(info.getTitle())) { + DOM.setInnerHTML(description, info.getTitle()); + DOM.setStyleAttribute(description, "display", ""); + hasContent = true; + } else { + DOM.setInnerHTML(description, ""); + DOM.setStyleAttribute(description, "display", "none"); + } + if (hasContent) { + setPopupPositionAndShow(new PositionCallback() { + public void setPosition(int offsetWidth, int offsetHeight) { + + if (offsetWidth > MAX_WIDTH) { + setWidth(MAX_WIDTH + "px"); + } + + offsetWidth = getOffsetWidth(); + + int x = tooltipEventMouseX + 10 + Window.getScrollLeft(); + int y = tooltipEventMouseY + 10 + Window.getScrollTop(); + + if (x + offsetWidth + MARGIN - Window.getScrollLeft() > Window + .getClientWidth()) { + x = Window.getClientWidth() - offsetWidth - MARGIN; + } + + if (y + offsetHeight + MARGIN - Window.getScrollTop() > Window + .getClientHeight()) { + y = tooltipEventMouseY - 5 - offsetHeight; + } + + setPopupPosition(x, y); + sinkEvents(Event.ONMOUSEOVER | Event.ONMOUSEOUT); + } + }); + } else { + hide(); + } + } + + public void showTooltip(Paintable owner, Event event) { + if (closing && tooltipOwner == owner) { + // return to same tooltip, cancel closing + closeTimer.cancel(); + closing = false; + justClosedTimer.cancel(); + justClosed = false; + return; + } + + if (closing) { + closeNow(); + } + + updatePosition(event); + + if (opening) { + showTimer.cancel(); + } + tooltipOwner = owner; + if (justClosed) { + showTimer.schedule(QUICK_OPEN_DELAY); + } else { + showTimer.schedule(OPEN_DELAY); + } + opening = true; + } + + private void closeNow() { + if (closing) { + hide(); + tooltipOwner = null; + setWidth(""); + closing = false; + } + } + + private Timer showTimer = new Timer() { + @Override + public void run() { + TooltipInfo info = ac.getTitleInfo(tooltipOwner); + if (null != info) { + show(info); + } + opening = false; + } + }; + + private Timer closeTimer = new Timer() { + @Override + public void run() { + closeNow(); + justClosedTimer.schedule(2000); + justClosed = true; + } + }; + + private Timer justClosedTimer = new Timer() { + @Override + public void run() { + justClosed = false; + } + }; + + public void hideTooltip() { + if (opening) { + showTimer.cancel(); + opening = false; + tooltipOwner = null; + } + if (!isAttached()) { + return; + } + if (closing) { + // already about to close + return; + } + closeTimer.schedule(CLOSE_TIMEOUT); + closing = true; + justClosed = true; + justClosedTimer.schedule(QUICK_OPEN_TIMEOUT); + + } + + private int tooltipEventMouseX; + private int tooltipEventMouseY; + + public void updatePosition(Event event) { + tooltipEventMouseX = DOM.eventGetClientX(event); + tooltipEventMouseY = DOM.eventGetClientY(event); + + } + + public void handleTooltipEvent(Event event, Paintable owner) { + final int type = DOM.eventGetType(event); + if ((VTooltip.TOOLTIP_EVENTS & type) == type) { + if (type == Event.ONMOUSEOVER) { + showTooltip(owner, event); + } else if (type == Event.ONMOUSEMOVE) { + updatePosition(event); + } else { + hideTooltip(); + } + } else { + // non-tooltip event, hide tooltip + hideTooltip(); + } + } + + @Override + public void onBrowserEvent(Event event) { + final int type = DOM.eventGetType(event); + // cancel closing event if tooltip is mouseovered; the user might want + // to scroll of cut&paste + + switch (type) { + case Event.ONMOUSEOVER: + closeTimer.cancel(); + closing = false; + break; + case Event.ONMOUSEOUT: + hideTooltip(); + break; + default: + // NOP + } + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IAbsoluteLayout.java b/src/com/vaadin/terminal/gwt/client/ui/IAbsoluteLayout.java deleted file mode 100644 index 06134e9515..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IAbsoluteLayout.java +++ /dev/null @@ -1,378 +0,0 @@ -package com.vaadin.terminal.gwt.client.ui; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.Map.Entry; - -import com.google.gwt.dom.client.DivElement; -import com.google.gwt.dom.client.Document; -import com.google.gwt.dom.client.Style; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.ui.ComplexPanel; -import com.google.gwt.user.client.ui.SimplePanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.Container; -import com.vaadin.terminal.gwt.client.ICaption; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.RenderSpace; -import com.vaadin.terminal.gwt.client.UIDL; - -public class IAbsoluteLayout extends ComplexPanel implements Container { - - /** Tag name for widget creation */ - public static final String TAGNAME = "absolutelayout"; - - /** Class name, prefix in styling */ - public static final String CLASSNAME = "i-absolutelayout"; - - private DivElement marginElement; - - protected final Element canvas = DOM.createDiv(); - - private int excessPixelsHorizontal; - - private int excessPixelsVertical; - - private Object previousStyleName; - - private Map pidToComponentWrappper = new HashMap(); - - protected ApplicationConnection client; - - private boolean rendering; - - public IAbsoluteLayout() { - setElement(Document.get().createDivElement()); - setStyleName(CLASSNAME); - marginElement = Document.get().createDivElement(); - canvas.getStyle().setProperty("position", "relative"); - marginElement.appendChild(canvas); - getElement().appendChild(marginElement); - } - - public RenderSpace getAllocatedSpace(Widget child) { - // TODO needs some special handling for components with only on edge - // horizontally or vertically defined - AbsoluteWrapper wrapper = (AbsoluteWrapper) child.getParent(); - int w; - if (wrapper.left != null && wrapper.right != null) { - w = wrapper.getOffsetWidth(); - } else if (wrapper.right != null) { - // left == null - // available width == right edge == offsetleft + width - w = wrapper.getOffsetWidth() + wrapper.getElement().getOffsetLeft(); - } else { - // left != null && right == null || left == null && - // right == null - // available width == canvas width - offset left - w = canvas.getOffsetWidth() - wrapper.getElement().getOffsetLeft(); - } - int h; - if (wrapper.top != null && wrapper.bottom != null) { - h = wrapper.getOffsetHeight(); - } else if (wrapper.bottom != null) { - // top not defined, available space 0... bottom of wrapper - h = wrapper.getElement().getOffsetTop() + wrapper.getOffsetHeight(); - } else { - // top defined or both undefined, available space == canvas - top - h = canvas.getOffsetHeight() - wrapper.getElement().getOffsetTop(); - } - - return new RenderSpace(w, h); - } - - public boolean hasChildComponent(Widget component) { - for (Iterator> iterator = pidToComponentWrappper - .entrySet().iterator(); iterator.hasNext();) { - if (iterator.next().getValue().paintable == component) { - return true; - } - } - return false; - } - - public void replaceChildComponent(Widget oldComponent, Widget newComponent) { - for (Widget wrapper : getChildren()) { - AbsoluteWrapper w = (AbsoluteWrapper) wrapper; - if (w.getWidget() == oldComponent) { - w.setWidget(newComponent); - return; - } - } - } - - public boolean requestLayout(Set children) { - // component inside an absolute panel never affects parent nor the - // layout - return true; - } - - public void updateCaption(Paintable component, UIDL uidl) { - AbsoluteWrapper parent2 = (AbsoluteWrapper) ((Widget) component) - .getParent(); - parent2.updateCaption(uidl); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - rendering = true; - this.client = client; - // TODO margin handling - if (client.updateComponent(this, uidl, true)) { - rendering = false; - return; - } - - HashSet unrenderedPids = new HashSet( - pidToComponentWrappper.keySet()); - - for (Iterator childIterator = uidl.getChildIterator(); childIterator - .hasNext();) { - UIDL cc = childIterator.next(); - UIDL componentUIDL = cc.getChildUIDL(0); - unrenderedPids.remove(componentUIDL.getId()); - getWrapper(client, componentUIDL).updateFromUIDL(cc); - } - - for (String pid : unrenderedPids) { - AbsoluteWrapper absoluteWrapper = pidToComponentWrappper.get(pid); - pidToComponentWrappper.remove(pid); - absoluteWrapper.destroy(); - } - rendering = false; - } - - private AbsoluteWrapper getWrapper(ApplicationConnection client, - UIDL componentUIDL) { - AbsoluteWrapper wrapper = pidToComponentWrappper.get(componentUIDL - .getId()); - if (wrapper == null) { - wrapper = new AbsoluteWrapper(client.getPaintable(componentUIDL)); - pidToComponentWrappper.put(componentUIDL.getId(), wrapper); - add(wrapper); - } - return wrapper; - - } - - @Override - public void add(Widget child) { - super.add(child, canvas); - } - - @Override - public void setStyleName(String style) { - super.setStyleName(style); - if (previousStyleName == null || !previousStyleName.equals(style)) { - excessPixelsHorizontal = -1; - excessPixelsVertical = -1; - } - } - - @Override - public void setWidth(String width) { - super.setWidth(width); - // TODO do this so that canvas gets the sized properly (the area - // inside marginals) - canvas.getStyle().setProperty("width", width); - - if (!rendering) { - if (BrowserInfo.get().isIE6()) { - relayoutWrappersForIe6(); - } - relayoutRelativeChildren(); - } - } - - private void relayoutRelativeChildren() { - for (Widget widget : getChildren()) { - if (widget instanceof AbsoluteWrapper) { - AbsoluteWrapper w = (AbsoluteWrapper) widget; - client.handleComponentRelativeSize(w.getWidget()); - w.updateCaptionPosition(); - } - } - } - - @Override - public void setHeight(String height) { - super.setHeight(height); - // TODO do this so that canvas gets the sized properly (the area - // inside marginals) - canvas.getStyle().setProperty("height", height); - - if (!rendering) { - if (BrowserInfo.get().isIE6()) { - relayoutWrappersForIe6(); - } - relayoutRelativeChildren(); - } - } - - private void relayoutWrappersForIe6() { - for (Widget wrapper : getChildren()) { - if (wrapper instanceof AbsoluteWrapper) { - ((AbsoluteWrapper) wrapper).ie6Layout(); - } - } - } - - public class AbsoluteWrapper extends SimplePanel { - private String css; - private String left; - private String top; - private String right; - private String bottom; - private String zIndex; - - private Paintable paintable; - private ICaption caption; - - public AbsoluteWrapper(Paintable paintable) { - this.paintable = paintable; - setStyleName(CLASSNAME + "-wrapper"); - } - - public void updateCaption(UIDL uidl) { - - boolean captionIsNeeded = ICaption.isNeeded(uidl); - if (captionIsNeeded) { - if (caption == null) { - caption = new ICaption(paintable, client); - IAbsoluteLayout.this.add(caption); - } - caption.updateCaption(uidl); - updateCaptionPosition(); - } else { - if (caption != null) { - caption.removeFromParent(); - caption = null; - } - } - } - - public void destroy() { - if (caption != null) { - caption.removeFromParent(); - } - client.unregisterPaintable(paintable); - removeFromParent(); - } - - public void updateFromUIDL(UIDL componentUIDL) { - setPosition(componentUIDL.getStringAttribute("css")); - if (getWidget() != paintable) { - setWidget((Widget) paintable); - } - UIDL childUIDL = componentUIDL.getChildUIDL(0); - paintable.updateFromUIDL(childUIDL, client); - if (childUIDL.hasAttribute("cached")) { - // child may need relative size adjustment if wrapper details - // have changed this could be optimized (check if wrapper size - // has changed) - client.handleComponentRelativeSize((Widget) paintable); - } - } - - public void setPosition(String stringAttribute) { - if (css == null || !css.equals(stringAttribute)) { - css = stringAttribute; - top = right = bottom = left = zIndex = null; - if (!css.equals("")) { - String[] properties = css.split(";"); - for (int i = 0; i < properties.length; i++) { - String[] keyValue = properties[i].split(":"); - if (keyValue[0].equals("left")) { - left = keyValue[1]; - } else if (keyValue[0].equals("top")) { - top = keyValue[1]; - } else if (keyValue[0].equals("right")) { - right = keyValue[1]; - } else if (keyValue[0].equals("bottom")) { - bottom = keyValue[1]; - } else if (keyValue[0].equals("z-index")) { - zIndex = keyValue[1]; - } - } - } - // ensure ne values - Style style = getElement().getStyle(); - style.setProperty("zIndex", zIndex); - style.setProperty("top", top); - style.setProperty("left", left); - style.setProperty("right", right); - style.setProperty("bottom", bottom); - - if (BrowserInfo.get().isIE6()) { - ie6Layout(); - } - } - updateCaptionPosition(); - } - - private void updateCaptionPosition() { - if (caption != null) { - Style style = caption.getElement().getStyle(); - style.setProperty("position", "absolute"); - style.setPropertyPx("left", getElement().getOffsetLeft()); - style.setPropertyPx("top", getElement().getOffsetTop() - - caption.getHeight()); - } - } - - private void ie6Layout() { - // special handling for IE6 is needed, it does not support - // setting both left/right or top/bottom - Style style = getElement().getStyle(); - if (bottom != null && top != null) { - // define height for wrapper to simulate bottom property - int bottompixels = measureForIE6(bottom); - ApplicationConnection.getConsole().log("ALB" + bottompixels); - int height = canvas.getOffsetHeight() - bottompixels - - getElement().getOffsetTop(); - ApplicationConnection.getConsole().log("ALB" + height); - if (height < 0) { - height = 0; - } - style.setPropertyPx("height", height); - } else { - // reset possibly existing value - style.setProperty("height", ""); - } - if (left != null && right != null) { - // define width for wrapper to simulate right property - int rightPixels = measureForIE6(right); - ApplicationConnection.getConsole().log("ALR" + rightPixels); - int width = canvas.getOffsetWidth() - rightPixels - - getElement().getOffsetWidth(); - ApplicationConnection.getConsole().log("ALR" + width); - if (width < 0) { - width = 0; - } - style.setPropertyPx("width", width); - } else { - // reset possibly existing value - style.setProperty("width", ""); - } - } - - } - - private Element measureElement; - - private int measureForIE6(String cssLength) { - if (measureElement == null) { - measureElement = DOM.createDiv(); - measureElement.getStyle().setProperty("position", "absolute"); - canvas.appendChild(measureElement); - } - measureElement.getStyle().setProperty("width", cssLength); - return measureElement.getOffsetWidth(); - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IAccordion.java b/src/com/vaadin/terminal/gwt/client/ui/IAccordion.java deleted file mode 100644 index 43c92cab32..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IAccordion.java +++ /dev/null @@ -1,647 +0,0 @@ -package com.vaadin.terminal.gwt.client.ui; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.ClickListener; -import com.google.gwt.user.client.ui.ComplexPanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.ContainerResizedListener; -import com.vaadin.terminal.gwt.client.ICaption; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.RenderInformation; -import com.vaadin.terminal.gwt.client.RenderSpace; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; - -public class IAccordion extends ITabsheetBase implements - ContainerResizedListener { - - public static final String CLASSNAME = "i-accordion"; - - private Set paintables = new HashSet(); - - private String height; - - private String width = ""; - - private HashMap lazyUpdateMap = new HashMap(); - - private RenderSpace renderSpace = new RenderSpace(0, 0, true); - - private StackItem openTab = null; - - private boolean rendering = false; - - private int selectedUIDLItemIndex = -1; - - private RenderInformation renderInformation = new RenderInformation(); - - public IAccordion() { - super(CLASSNAME); - // IE6 needs this to calculate offsetHeight correctly - if (BrowserInfo.get().isIE6()) { - DOM.setStyleAttribute(getElement(), "zoom", "1"); - } - } - - @Override - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - rendering = true; - selectedUIDLItemIndex = -1; - super.updateFromUIDL(uidl, client); - /* - * Render content after all tabs have been created and we know how large - * the content area is - */ - if (selectedUIDLItemIndex >= 0) { - StackItem selectedItem = getStackItem(selectedUIDLItemIndex); - UIDL selectedTabUIDL = lazyUpdateMap.remove(selectedItem); - open(selectedUIDLItemIndex); - - selectedItem.setContent(selectedTabUIDL); - } else if (!uidl.getBooleanAttribute("cached") && openTab != null) { - close(openTab); - } - - iLayout(); - // finally render possible hidden tabs - if (lazyUpdateMap.size() > 0) { - for (Iterator iterator = lazyUpdateMap.keySet().iterator(); iterator - .hasNext();) { - StackItem item = (StackItem) iterator.next(); - item.setContent(lazyUpdateMap.get(item)); - } - lazyUpdateMap.clear(); - } - - renderInformation.updateSize(getElement()); - - rendering = false; - } - - @Override - protected void renderTab(UIDL tabUidl, int index, boolean selected, - boolean hidden) { - StackItem item; - int itemIndex; - if (getWidgetCount() <= index) { - // Create stackItem and render caption - item = new StackItem(tabUidl); - if (getWidgetCount() == 0) { - item.addStyleDependentName("first"); - } - itemIndex = getWidgetCount(); - add(item, getElement()); - } else { - item = getStackItem(index); - item = moveStackItemIfNeeded(item, index, tabUidl); - itemIndex = index; - } - item.updateCaption(tabUidl); - - item.setVisible(!hidden); - - if (selected) { - selectedUIDLItemIndex = itemIndex; - } - - if (tabUidl.getChildCount() > 0) { - lazyUpdateMap.put(item, tabUidl.getChildUIDL(0)); - } - } - - /** - * This method tries to find out if a tab has been rendered with a different - * index previously. If this is the case it re-orders the children so the - * same StackItem is used for rendering this time. E.g. if the first tab has - * been removed all tabs which contain cached content must be moved 1 step - * up to preserve the cached content. - * - * @param item - * @param newIndex - * @param tabUidl - * @return - */ - private StackItem moveStackItemIfNeeded(StackItem item, int newIndex, - UIDL tabUidl) { - UIDL tabContentUIDL = null; - Paintable tabContent = null; - if (tabUidl.getChildCount() > 0) { - tabContentUIDL = tabUidl.getChildUIDL(0); - tabContent = client.getPaintable(tabContentUIDL); - } - - Widget itemWidget = item.getComponent(); - if (tabContent != null) { - if (tabContent != itemWidget) { - /* - * This is not the same widget as before, find out if it has - * been moved - */ - int oldIndex = -1; - StackItem oldItem = null; - for (int i = 0; i < getWidgetCount(); i++) { - Widget w = getWidget(i); - oldItem = (StackItem) w; - if (tabContent == oldItem.getComponent()) { - oldIndex = i; - break; - } - } - - if (oldIndex != -1 && oldIndex > newIndex) { - /* - * The tab has previously been rendered in another position - * so we must move the cached content to correct position. - * We move only items with oldIndex > newIndex to prevent - * moving items already rendered in this update. If for - * instance tabs 1,2,3 are removed and added as 3,2,1 we - * cannot re-use "1" when we get to the third tab. - */ - insert(oldItem, getElement(), newIndex, true); - return oldItem; - } - } - } else { - // Tab which has never been loaded. Must assure we use an empty - // StackItem - Widget oldWidget = item.getComponent(); - if (oldWidget != null) { - item = new StackItem(tabUidl); - insert(item, getElement(), newIndex, true); - } - } - return item; - } - - private void open(int itemIndex) { - StackItem item = (StackItem) getWidget(itemIndex); - boolean alreadyOpen = false; - if (openTab != null) { - if (openTab.isOpen()) { - if (openTab == item) { - alreadyOpen = true; - } else { - openTab.close(); - } - } - } - - if (!alreadyOpen) { - item.open(); - activeTabIndex = itemIndex; - openTab = item; - } - - // Update the size for the open tab - updateOpenTabSize(); - } - - private void close(StackItem item) { - if (!item.isOpen()) { - return; - } - - item.close(); - activeTabIndex = -1; - openTab = null; - - } - - @Override - protected void selectTab(final int index, final UIDL contentUidl) { - StackItem item = getStackItem(index); - if (index != activeTabIndex) { - open(index); - iLayout(); - // TODO Check if this is needed - client.runDescendentsLayout(this); - - } - item.setContent(contentUidl); - } - - public void onSelectTab(StackItem item) { - final int index = getWidgetIndex(item); - if (index != activeTabIndex && !disabled && !readonly - && !disabledTabKeys.contains(tabKeys.get(index))) { - addStyleDependentName("loading"); - client - .updateVariable(id, "selected", "" + tabKeys.get(index), - true); - } - } - - @Override - public void setWidth(String width) { - if (this.width.equals(width)) { - return; - } - - super.setWidth(width); - this.width = width; - if (!rendering) { - updateOpenTabSize(); - - if (isDynamicHeight()) { - Util.updateRelativeChildrenAndSendSizeUpdateEvent(client, - openTab, this); - updateOpenTabSize(); - } - - if (isDynamicHeight()) { - openTab.setHeightFromWidget(); - } - iLayout(); - } - } - - @Override - public void setHeight(String height) { - super.setHeight(height); - this.height = height; - - if (!rendering) { - updateOpenTabSize(); - } - - } - - /** - * Sets the size of the open tab - */ - private void updateOpenTabSize() { - if (openTab == null) { - renderSpace.setHeight(0); - renderSpace.setWidth(0); - return; - } - - // WIDTH - if (!isDynamicWidth()) { - int w = getOffsetWidth(); - openTab.setWidth(w); - renderSpace.setWidth(w); - } else { - renderSpace.setWidth(0); - } - - // HEIGHT - if (!isDynamicHeight()) { - int usedPixels = 0; - for (Widget w : getChildren()) { - StackItem item = (StackItem) w; - if (item == openTab) { - usedPixels += item.getCaptionHeight(); - } else { - // This includes the captionNode borders - usedPixels += item.getHeight(); - } - } - - int offsetHeight = getOffsetHeight(); - - int spaceForOpenItem = offsetHeight - usedPixels; - - if (spaceForOpenItem < 0) { - spaceForOpenItem = 0; - } - - renderSpace.setHeight(spaceForOpenItem); - openTab.setHeight(spaceForOpenItem); - } else { - renderSpace.setHeight(0); - openTab.setHeightFromWidget(); - - } - - } - - public void iLayout() { - if (openTab == null) { - return; - } - - if (isDynamicWidth()) { - int maxWidth = 40; - for (Widget w : getChildren()) { - StackItem si = (StackItem) w; - int captionWidth = si.getCaptionWidth(); - if (captionWidth > maxWidth) { - maxWidth = captionWidth; - } - } - int widgetWidth = openTab.getWidgetWidth(); - if (widgetWidth > maxWidth) { - maxWidth = widgetWidth; - } - super.setWidth(maxWidth + "px"); - openTab.setWidth(maxWidth); - } - - Util.runWebkitOverflowAutoFix(openTab.getContainerElement()); - - } - - /** - * - */ - protected class StackItem extends ComplexPanel implements ClickListener { - - public void setHeight(int height) { - if (height == -1) { - super.setHeight(""); - DOM.setStyleAttribute(content, "height", "0px"); - } else { - super.setHeight((height + getCaptionHeight()) + "px"); - DOM.setStyleAttribute(content, "height", height + "px"); - DOM - .setStyleAttribute(content, "top", getCaptionHeight() - + "px"); - - } - } - - public Widget getComponent() { - if (getWidgetCount() < 2) { - return null; - } - return getWidget(1); - } - - @Override - public void setVisible(boolean visible) { - super.setVisible(visible); - } - - public void setHeightFromWidget() { - Widget paintable = getPaintable(); - if (paintable == null) { - return; - } - - int paintableHeight = (paintable).getElement().getOffsetHeight(); - setHeight(paintableHeight); - - } - - /** - * Returns caption width including padding - * - * @return - */ - public int getCaptionWidth() { - if (caption == null) { - return 0; - } - - int captionWidth = caption.getRequiredWidth(); - int padding = Util.measureHorizontalPaddingAndBorder(caption - .getElement(), 18); - return captionWidth + padding; - } - - public void setWidth(int width) { - if (width == -1) { - super.setWidth(""); - } else { - super.setWidth(width + "px"); - } - } - - public int getHeight() { - return getOffsetHeight(); - } - - public int getCaptionHeight() { - return captionNode.getOffsetHeight(); - } - - private ICaption caption; - private boolean open = false; - private Element content = DOM.createDiv(); - private Element captionNode = DOM.createDiv(); - - public StackItem(UIDL tabUidl) { - setElement(DOM.createDiv()); - caption = new ICaption(null, client); - caption.addClickListener(this); - if (BrowserInfo.get().isIE6()) { - DOM.setEventListener(captionNode, this); - DOM.sinkEvents(captionNode, Event.BUTTON_LEFT); - } - super.add(caption, captionNode); - DOM.appendChild(captionNode, caption.getElement()); - DOM.appendChild(getElement(), captionNode); - DOM.appendChild(getElement(), content); - setStylePrimaryName(CLASSNAME + "-item"); - DOM.setElementProperty(content, "className", CLASSNAME - + "-item-content"); - DOM.setElementProperty(captionNode, "className", CLASSNAME - + "-item-caption"); - close(); - } - - @Override - public void onBrowserEvent(Event event) { - onSelectTab(this); - } - - public Element getContainerElement() { - return content; - } - - public Widget getPaintable() { - if (getWidgetCount() > 1) { - return getWidget(1); - } else { - return null; - } - } - - public void replacePaintable(Paintable newPntbl) { - if (getWidgetCount() > 1) { - client.unregisterPaintable((Paintable) getWidget(1)); - paintables.remove(getWidget(1)); - remove(1); - } - add((Widget) newPntbl, content); - paintables.add(newPntbl); - } - - public void open() { - open = true; - DOM.setStyleAttribute(content, "top", getCaptionHeight() + "px"); - DOM.setStyleAttribute(content, "left", "0px"); - DOM.setStyleAttribute(content, "visibility", ""); - addStyleDependentName("open"); - } - - public void hide() { - DOM.setStyleAttribute(content, "visibility", "hidden"); - } - - public void close() { - DOM.setStyleAttribute(content, "visibility", "hidden"); - DOM.setStyleAttribute(content, "top", "-100000px"); - DOM.setStyleAttribute(content, "left", "-100000px"); - removeStyleDependentName("open"); - setHeight(-1); - open = false; - } - - public boolean isOpen() { - return open; - } - - public void setContent(UIDL contentUidl) { - final Paintable newPntbl = client.getPaintable(contentUidl); - if (getPaintable() == null) { - add((Widget) newPntbl, content); - paintables.add(newPntbl); - } else if (getPaintable() != newPntbl) { - replacePaintable(newPntbl); - } - newPntbl.updateFromUIDL(contentUidl, client); - if (contentUidl.getBooleanAttribute("cached")) { - /* - * The size of a cached, relative sized component must be - * updated to report correct size. - */ - client.handleComponentRelativeSize((Widget) newPntbl); - } - if (isOpen() && isDynamicHeight()) { - setHeightFromWidget(); - } - } - - public void onClick(Widget sender) { - onSelectTab(this); - } - - public void updateCaption(UIDL uidl) { - caption.updateCaption(uidl); - } - - public int getWidgetWidth() { - return DOM.getFirstChild(content).getOffsetWidth(); - } - - public boolean contains(Paintable p) { - return (getPaintable() == p); - } - - public boolean isCaptionVisible() { - return caption.isVisible(); - } - - } - - @Override - protected void clearPaintables() { - clear(); - } - - public boolean isDynamicHeight() { - return height == null || height.equals(""); - } - - public boolean isDynamicWidth() { - return width == null || width.equals(""); - } - - @Override - protected Iterator getPaintableIterator() { - return paintables.iterator(); - } - - public boolean hasChildComponent(Widget component) { - if (paintables.contains(component)) { - return true; - } else { - return false; - } - } - - public void replaceChildComponent(Widget oldComponent, Widget newComponent) { - for (Widget w : getChildren()) { - StackItem item = (StackItem) w; - if (item.getPaintable() == oldComponent) { - item.replacePaintable((Paintable) newComponent); - return; - } - } - } - - public void updateCaption(Paintable component, UIDL uidl) { - /* Accordion does not render its children's captions */ - } - - public boolean requestLayout(Set child) { - if (!isDynamicHeight() && !isDynamicWidth()) { - /* - * If the height and width has been specified for this container the - * child components cannot make the size of the layout change - */ - - return true; - } - - updateOpenTabSize(); - - if (renderInformation.updateSize(getElement())) { - /* - * Size has changed so we let the child components know about the - * new size. - */ - iLayout(); - // TODO Check if this is needed - client.runDescendentsLayout(this); - - return false; - } else { - /* - * Size has not changed so we do not need to propagate the event - * further - */ - return true; - } - - } - - public RenderSpace getAllocatedSpace(Widget child) { - return renderSpace; - } - - @Override - protected int getTabCount() { - return getWidgetCount(); - } - - @Override - protected void removeTab(int index) { - StackItem item = getStackItem(index); - remove(item); - } - - @Override - protected Paintable getTab(int index) { - if (index < getWidgetCount()) { - return (Paintable) (getStackItem(index)).getPaintable(); - } - - return null; - } - - private StackItem getStackItem(int index) { - return (StackItem) getWidget(index); - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IButton.java b/src/com/vaadin/terminal/gwt/client/ui/IButton.java deleted file mode 100644 index 4a1dcbad07..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IButton.java +++ /dev/null @@ -1,190 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.Button; -import com.google.gwt.user.client.ui.ClickListener; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.ITooltip; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; - -public class IButton extends Button implements Paintable { - - private String width = null; - - public static final String CLASSNAME = "i-button"; - - // Used only for IE, because it doesn't support :active CSS selector - private static final String CLASSNAME_DOWN = "i-pressed"; - - String id; - - ApplicationConnection client; - - private Element errorIndicatorElement; - - private final Element captionElement = DOM.createSpan(); - - private Icon icon; - - /** - * Helper flat to handle special-case where the button is moved from under - * mouse while clicking it. In this case mouse leaves the button without - * moving. - */ - private boolean clickPending; - - public IButton() { - setStyleName(CLASSNAME); - - DOM.appendChild(getElement(), captionElement); - - addClickListener(new ClickListener() { - public void onClick(Widget sender) { - if (id == null || client == null) { - return; - } - /* - * TODO isolate workaround. Safari don't always seem to fire - * onblur previously focused component before button is clicked. - */ - IButton.this.setFocus(true); - client.updateVariable(id, "state", true, true); - clickPending = false; - } - }); - sinkEvents(ITooltip.TOOLTIP_EVENTS); - sinkEvents(Event.ONMOUSEDOWN); - sinkEvents(Event.ONMOUSEUP); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - - // Ensure correct implementation, - // but don't let container manage caption etc. - if (client.updateComponent(this, uidl, false)) { - return; - } - - // Save details - this.client = client; - id = uidl.getId(); - - // Set text - setText(uidl.getStringAttribute("caption")); - - // handle error - if (uidl.hasAttribute("error")) { - if (errorIndicatorElement == null) { - errorIndicatorElement = DOM.createDiv(); - DOM.setElementProperty(errorIndicatorElement, "className", - "i-errorindicator"); - } - DOM.insertChild(getElement(), errorIndicatorElement, 0); - - // Fix for IE6, IE7 - if (BrowserInfo.get().isIE()) { - DOM.setInnerText(errorIndicatorElement, " "); - } - - } else if (errorIndicatorElement != null) { - DOM.removeChild(getElement(), errorIndicatorElement); - errorIndicatorElement = null; - } - - if (uidl.hasAttribute("readonly")) { - setEnabled(false); - } - - if (uidl.hasAttribute("icon")) { - if (icon == null) { - icon = new Icon(client); - DOM.insertChild(getElement(), icon.getElement(), 0); - } - icon.setUri(uidl.getStringAttribute("icon")); - } else { - if (icon != null) { - DOM.removeChild(getElement(), icon.getElement()); - icon = null; - } - } - if (BrowserInfo.get().isIE7()) { - if (width.equals("")) { - setWidth(getOffsetWidth() + "px"); - } - } - } - - @Override - public void setText(String text) { - DOM.setInnerText(captionElement, text); - } - - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - - if (DOM.eventGetType(event) == Event.ONLOAD) { - Util.notifyParentOfSizeChange(this, true); - - } else if (DOM.eventGetType(event) == Event.ONMOUSEDOWN - && event.getButton() == Event.BUTTON_LEFT) { - clickPending = true; - if (BrowserInfo.get().isIE()) { - // Only for IE, because it doesn't support :active CSS selector - // Simple check is cheaper than DOM manipulation - addStyleName(CLASSNAME_DOWN); - } - } else if (DOM.eventGetType(event) == Event.ONMOUSEMOVE) { - clickPending = false; - } else if (DOM.eventGetType(event) == Event.ONMOUSEOUT) { - if (clickPending) { - click(); - } - if (BrowserInfo.get().isIE()) { - removeStyleName(CLASSNAME_DOWN); - } - clickPending = false; - } else if (DOM.eventGetType(event) == Event.ONMOUSEUP) { - if (BrowserInfo.get().isIE()) { - removeStyleName(CLASSNAME_DOWN); - } - } - - if (client != null) { - client.handleTooltipEvent(event, this); - } - } - - @Override - public void setWidth(String width) { - /* Workaround for IE7 button size part 1 (#2014) */ - if (BrowserInfo.get().isIE7() && this.width != null) { - if (this.width.equals(width)) { - return; - } - - if (width == null) { - width = ""; - } - } - - this.width = width; - super.setWidth(width); - - /* Workaround for IE7 button size part 2 (#2014) */ - if (BrowserInfo.get().isIE7()) { - super.setWidth(width); - } - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ICalendarPanel.java b/src/com/vaadin/terminal/gwt/client/ui/ICalendarPanel.java deleted file mode 100644 index 31566a75b9..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ICalendarPanel.java +++ /dev/null @@ -1,520 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.Date; -import java.util.Iterator; -import java.util.List; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.ui.FlexTable; -import com.google.gwt.user.client.ui.MouseListener; -import com.google.gwt.user.client.ui.MouseListenerCollection; -import com.google.gwt.user.client.ui.SourcesMouseEvents; -import com.google.gwt.user.client.ui.SourcesTableEvents; -import com.google.gwt.user.client.ui.TableListener; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.DateTimeService; -import com.vaadin.terminal.gwt.client.LocaleService; - -public class ICalendarPanel extends FlexTable implements MouseListener { - - private final IDateField datefield; - - private IEventButton prevYear; - - private IEventButton nextYear; - - private IEventButton prevMonth; - - private IEventButton nextMonth; - - private ITime time; - - private Date minDate = null; - - private Date maxDate = null; - - private CalendarEntrySource entrySource; - - /* Needed to identify resolution changes */ - private int resolution = IDateField.RESOLUTION_YEAR; - - /* Needed to identify locale changes */ - private String locale = LocaleService.getDefaultLocale(); - - public ICalendarPanel(IDateField parent) { - datefield = parent; - setStyleName(IDateField.CLASSNAME + "-calendarpanel"); - // buildCalendar(true); - addTableListener(new DateClickListener(this)); - } - - public ICalendarPanel(IDateField parent, Date min, Date max) { - datefield = parent; - setStyleName(IDateField.CLASSNAME + "-calendarpanel"); - // buildCalendar(true); - addTableListener(new DateClickListener(this)); - - } - - private void buildCalendar(boolean forceRedraw) { - final boolean needsMonth = datefield.getCurrentResolution() > IDateField.RESOLUTION_YEAR; - boolean needsBody = datefield.getCurrentResolution() >= IDateField.RESOLUTION_DAY; - final boolean needsTime = datefield.getCurrentResolution() >= IDateField.RESOLUTION_HOUR; - forceRedraw = prevYear == null ? true : forceRedraw; - buildCalendarHeader(forceRedraw, needsMonth); - clearCalendarBody(!needsBody); - if (needsBody) { - buildCalendarBody(); - } - if (needsTime) { - buildTime(forceRedraw); - } else if (time != null) { - remove(time); - time = null; - } - } - - private void clearCalendarBody(boolean remove) { - if (!remove) { - for (int row = 2; row < 8; row++) { - for (int col = 0; col < 7; col++) { - setHTML(row, col, " "); - } - } - } else if (getRowCount() > 2) { - while (getRowCount() > 2) { - removeRow(2); - } - } - } - - private void buildCalendarHeader(boolean forceRedraw, boolean needsMonth) { - if (forceRedraw) { - if (prevMonth == null) { // Only do once - prevYear = new IEventButton(); - prevYear.setHTML("«"); - prevYear.setStyleName("i-button-prevyear"); - nextYear = new IEventButton(); - nextYear.setHTML("»"); - nextYear.setStyleName("i-button-nextyear"); - prevYear.addMouseListener(this); - nextYear.addMouseListener(this); - setWidget(0, 0, prevYear); - setWidget(0, 4, nextYear); - - if (needsMonth) { - prevMonth = new IEventButton(); - prevMonth.setHTML("‹"); - prevMonth.setStyleName("i-button-prevmonth"); - nextMonth = new IEventButton(); - nextMonth.setHTML("›"); - nextMonth.setStyleName("i-button-nextmonth"); - prevMonth.addMouseListener(this); - nextMonth.addMouseListener(this); - setWidget(0, 3, nextMonth); - setWidget(0, 1, prevMonth); - } - - getFlexCellFormatter().setColSpan(0, 2, 3); - getRowFormatter().addStyleName(0, - IDateField.CLASSNAME + "-calendarpanel-header"); - } else if (!needsMonth) { - // Remove month traverse buttons - prevMonth.removeMouseListener(this); - nextMonth.removeMouseListener(this); - remove(prevMonth); - remove(nextMonth); - prevMonth = null; - nextMonth = null; - } - - // Print weekday names - final int firstDay = datefield.getDateTimeService() - .getFirstDayOfWeek(); - for (int i = 0; i < 7; i++) { - int day = i + firstDay; - if (day > 6) { - day = 0; - } - if (datefield.getCurrentResolution() > IDateField.RESOLUTION_MONTH) { - setHTML(1, i, "" - + datefield.getDateTimeService().getShortDay(day) - + ""); - } else { - setHTML(1, i, ""); - } - } - } - - final String monthName = needsMonth ? datefield.getDateTimeService() - .getMonth(datefield.getShowingDate().getMonth()) : ""; - final int year = datefield.getShowingDate().getYear() + 1900; - setHTML(0, 2, "" + monthName + " " + year - + ""); - } - - private void buildCalendarBody() { - // date actually selected? - Date currentDate = datefield.getCurrentDate(); - Date showing = datefield.getShowingDate(); - boolean selected = (currentDate != null - && currentDate.getMonth() == showing.getMonth() && currentDate - .getYear() == showing.getYear()); - - final int startWeekDay = datefield.getDateTimeService() - .getStartWeekDay(datefield.getShowingDate()); - final int numDays = DateTimeService.getNumberOfDaysInMonth(datefield - .getShowingDate()); - int dayCount = 0; - final Date today = new Date(); - final Date curr = new Date(datefield.getShowingDate().getTime()); - for (int row = 2; row < 8; row++) { - for (int col = 0; col < 7; col++) { - if (!(row == 2 && col < startWeekDay)) { - if (dayCount < numDays) { - final int selectedDate = ++dayCount; - String title = ""; - if (entrySource != null) { - curr.setDate(dayCount); - final List entries = entrySource.getEntries(curr, - IDateField.RESOLUTION_DAY); - if (entries != null) { - for (final Iterator it = entries.iterator(); it - .hasNext();) { - final CalendarEntry entry = (CalendarEntry) it - .next(); - title += (title.length() > 0 ? ", " : "") - + entry.getStringForDate(curr); - } - } - } - final String baseclass = IDateField.CLASSNAME - + "-calendarpanel-day"; - String cssClass = baseclass; - if (!isEnabledDate(curr)) { - cssClass += " " + baseclass + "-disabled"; - } - if (selected - && datefield.getShowingDate().getDate() == dayCount) { - cssClass += " " + baseclass + "-selected"; - } - if (today.getDate() == dayCount - && today.getMonth() == datefield - .getShowingDate().getMonth() - && today.getYear() == datefield - .getShowingDate().getYear()) { - cssClass += " " + baseclass + "-today"; - } - if (title.length() > 0) { - cssClass += " " + baseclass + "-entry"; - } - setHTML(row, col, "" - + selectedDate + ""); - } else { - break; - } - - } - } - } - } - - private void buildTime(boolean forceRedraw) { - if (time == null) { - time = new ITime(datefield); - setText(8, 0, ""); // Add new row - getFlexCellFormatter().setColSpan(8, 0, 7); - setWidget(8, 0, time); - } - time.updateTime(forceRedraw); - } - - /** - * - * @param forceRedraw - * Build all from scratch, in case of e.g. locale changes - */ - public void updateCalendar() { - // Locale and resolution changes force a complete redraw - buildCalendar(locale != datefield.getCurrentLocale() - || resolution != datefield.getCurrentResolution()); - if (datefield instanceof ITextualDate) { - ((ITextualDate) datefield).buildDate(); - } - locale = datefield.getCurrentLocale(); - resolution = datefield.getCurrentResolution(); - } - - private boolean isEnabledDate(Date date) { - if ((minDate != null && date.before(minDate)) - || (maxDate != null && date.after(maxDate))) { - return false; - } - return true; - } - - private void processClickEvent(Widget sender, boolean updateVariable) { - if (!datefield.isEnabled() || datefield.isReadonly()) { - return; - } - Date showingDate = datefield.getShowingDate(); - if (!updateVariable) { - if (sender == prevYear) { - showingDate.setYear(showingDate.getYear() - 1); - updateCalendar(); - } else if (sender == nextYear) { - showingDate.setYear(showingDate.getYear() + 1); - updateCalendar(); - } else if (sender == prevMonth) { - int currentMonth = showingDate.getMonth(); - showingDate.setMonth(currentMonth - 1); - - /* - * If the selected date was e.g. 31.12 the new date would be - * 31.11 but this date is invalid so the new date will be 1.12. - * This is taken care of by decreasing the date until we have - * the correct month. - */ - while (showingDate.getMonth() == currentMonth) { - showingDate.setDate(showingDate.getDate() - 1); - } - - updateCalendar(); - } else if (sender == nextMonth) { - int currentMonth = showingDate.getMonth(); - showingDate.setMonth(currentMonth + 1); - int requestedMonth = (currentMonth + 1) % 12; - - /* - * If the selected date was e.g. 31.3 the new date would be 31.4 - * but this date is invalid so the new date will be 1.5. This is - * taken care of by decreasing the date until we have the - * correct month. - */ - while (showingDate.getMonth() != requestedMonth) { - showingDate.setDate(showingDate.getDate() - 1); - } - - updateCalendar(); - } - } else { - if (datefield.getCurrentResolution() == IDateField.RESOLUTION_YEAR - || datefield.getCurrentResolution() == IDateField.RESOLUTION_MONTH) { - // Due to current UI, update variable if res=year/month - datefield.setCurrentDate(new Date(showingDate.getTime())); - if (datefield.getCurrentResolution() == IDateField.RESOLUTION_MONTH) { - datefield.getClient().updateVariable(datefield.getId(), - "month", datefield.getCurrentDate().getMonth() + 1, - false); - } - datefield.getClient().updateVariable(datefield.getId(), "year", - datefield.getCurrentDate().getYear() + 1900, - datefield.isImmediate()); - - /* Must update the value in the textfield also */ - updateCalendar(); - } - } - } - - private Timer timer; - - public void onMouseDown(final Widget sender, int x, int y) { - // Allow user to click-n-hold for fast-forward or fast-rewind. - // Timer is first used for a 500ms delay after mousedown. After that has - // elapsed, another timer is triggered to go off every 150ms. Both - // timers are cancelled on mouseup or mouseout. - if (sender instanceof IEventButton) { - processClickEvent(sender, false); - timer = new Timer() { - @Override - public void run() { - timer = new Timer() { - @Override - public void run() { - processClickEvent(sender, false); - } - }; - timer.scheduleRepeating(150); - } - }; - timer.schedule(500); - } - } - - public void onMouseEnter(Widget sender) { - } - - public void onMouseLeave(Widget sender) { - if (timer != null) { - timer.cancel(); - } - } - - public void onMouseMove(Widget sender, int x, int y) { - } - - public void onMouseUp(Widget sender, int x, int y) { - if (timer != null) { - timer.cancel(); - } - processClickEvent(sender, true); - } - - private class IEventButton extends IButton implements SourcesMouseEvents { - - private MouseListenerCollection mouseListeners; - - public IEventButton() { - super(); - sinkEvents(Event.FOCUSEVENTS | Event.KEYEVENTS | Event.ONCLICK - | Event.MOUSEEVENTS); - } - - public void addMouseListener(MouseListener listener) { - if (mouseListeners == null) { - mouseListeners = new MouseListenerCollection(); - } - mouseListeners.add(listener); - } - - public void removeMouseListener(MouseListener listener) { - if (mouseListeners != null) { - mouseListeners.remove(listener); - } - } - - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - switch (DOM.eventGetType(event)) { - case Event.ONMOUSEDOWN: - case Event.ONMOUSEUP: - case Event.ONMOUSEMOVE: - case Event.ONMOUSEOVER: - case Event.ONMOUSEOUT: - if (mouseListeners != null) { - mouseListeners.fireMouseEvent(this, event); - } - break; - } - } - } - - private class DateClickListener implements TableListener { - - private final ICalendarPanel cal; - - public DateClickListener(ICalendarPanel panel) { - cal = panel; - } - - public void onCellClicked(SourcesTableEvents sender, int row, int col) { - if (sender != cal || row < 2 || row > 7 - || !cal.datefield.isEnabled() || cal.datefield.isReadonly()) { - return; - } - - final String text = cal.getText(row, col); - if (text.equals(" ")) { - return; - } - - try { - final Integer day = new Integer(text); - final Date newDate = cal.datefield.getShowingDate(); - newDate.setDate(day.intValue()); - if (!isEnabledDate(newDate)) { - return; - } - if (cal.datefield.getCurrentDate() == null) { - cal.datefield.setCurrentDate(new Date(newDate.getTime())); - - // Init variables with current time - datefield.getClient().updateVariable(cal.datefield.getId(), - "hour", newDate.getHours(), false); - datefield.getClient().updateVariable(cal.datefield.getId(), - "min", newDate.getMinutes(), false); - datefield.getClient().updateVariable(cal.datefield.getId(), - "sec", newDate.getSeconds(), false); - datefield.getClient().updateVariable(cal.datefield.getId(), - "msec", datefield.getMilliseconds(), false); - } - - cal.datefield.getCurrentDate().setTime(newDate.getTime()); - cal.datefield.getClient().updateVariable(cal.datefield.getId(), - "day", cal.datefield.getCurrentDate().getDate(), false); - cal.datefield.getClient().updateVariable(cal.datefield.getId(), - "month", cal.datefield.getCurrentDate().getMonth() + 1, - false); - cal.datefield.getClient().updateVariable(cal.datefield.getId(), - "year", - cal.datefield.getCurrentDate().getYear() + 1900, - cal.datefield.isImmediate()); - - if (datefield instanceof ITextualDate - && resolution < IDateField.RESOLUTION_HOUR) { - ((IToolkitOverlay) getParent()).hide(); - } else { - updateCalendar(); - } - - } catch (final NumberFormatException e) { - // Not a number, ignore and stop here - return; - } - } - - } - - public void setLimits(Date min, Date max) { - if (min != null) { - final Date d = new Date(min.getTime()); - d.setHours(0); - d.setMinutes(0); - d.setSeconds(1); - minDate = d; - } else { - minDate = null; - } - if (max != null) { - final Date d = new Date(max.getTime()); - d.setHours(24); - d.setMinutes(59); - d.setSeconds(59); - maxDate = d; - } else { - maxDate = null; - } - } - - public void setCalendarEntrySource(CalendarEntrySource entrySource) { - this.entrySource = entrySource; - } - - public CalendarEntrySource getCalendarEntrySource() { - return entrySource; - } - - public interface CalendarEntrySource { - public List getEntries(Date date, int resolution); - } - - /** - * Sets focus to Calendar panel. - * - * @param focus - */ - public void setFocus(boolean focus) { - nextYear.setFocus(focus); - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ICheckBox.java b/src/com/vaadin/terminal/gwt/client/ui/ICheckBox.java deleted file mode 100644 index 397dc89828..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ICheckBox.java +++ /dev/null @@ -1,140 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.ClickListener; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.ITooltip; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; - -public class ICheckBox extends com.google.gwt.user.client.ui.CheckBox implements - Paintable, Field { - - public static final String CLASSNAME = "i-checkbox"; - - String id; - - boolean immediate; - - ApplicationConnection client; - - private Element errorIndicatorElement; - - private Icon icon; - - private boolean isBlockMode = false; - - public ICheckBox() { - setStyleName(CLASSNAME); - addClickListener(new ClickListener() { - - public void onClick(Widget sender) { - if (id == null || client == null) { - return; - } - client.updateVariable(id, "state", isChecked(), immediate); - } - - }); - sinkEvents(ITooltip.TOOLTIP_EVENTS); - Element el = DOM.getFirstChild(getElement()); - while (el != null) { - DOM.sinkEvents(el, - (DOM.getEventsSunk(el) | ITooltip.TOOLTIP_EVENTS)); - el = DOM.getNextSibling(el); - } - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - // Save details - this.client = client; - id = uidl.getId(); - - // Ensure correct implementation - if (client.updateComponent(this, uidl, false)) { - return; - } - - if (uidl.hasAttribute("error")) { - if (errorIndicatorElement == null) { - errorIndicatorElement = DOM.createDiv(); - errorIndicatorElement.setInnerHTML(" "); - DOM.setElementProperty(errorIndicatorElement, "className", - "i-errorindicator"); - DOM.appendChild(getElement(), errorIndicatorElement); - DOM.sinkEvents(errorIndicatorElement, ITooltip.TOOLTIP_EVENTS - | Event.ONCLICK); - } - } else if (errorIndicatorElement != null) { - DOM.setStyleAttribute(errorIndicatorElement, "display", "none"); - } - - if (uidl.hasAttribute("readonly")) { - setEnabled(false); - } - - if (uidl.hasAttribute("icon")) { - if (icon == null) { - icon = new Icon(client); - DOM.insertChild(getElement(), icon.getElement(), 1); - icon.sinkEvents(ITooltip.TOOLTIP_EVENTS); - icon.sinkEvents(Event.ONCLICK); - } - icon.setUri(uidl.getStringAttribute("icon")); - } else if (icon != null) { - // detach icon - DOM.removeChild(getElement(), icon.getElement()); - icon = null; - } - - // Set text - setText(uidl.getStringAttribute("caption")); - setChecked(uidl.getBooleanVariable("state")); - immediate = uidl.getBooleanAttribute("immediate"); - } - - @Override - public void onBrowserEvent(Event event) { - if (icon != null && (event.getTypeInt() == Event.ONCLICK) - && (event.getTarget() == icon.getElement())) { - setChecked(!isChecked()); - } - super.onBrowserEvent(event); - if (event.getTypeInt() == Event.ONLOAD) { - Util.notifyParentOfSizeChange(this, true); - } - if (client != null) { - client.handleTooltipEvent(event, this); - } - } - - @Override - public void setWidth(String width) { - setBlockMode(); - super.setWidth(width); - } - - @Override - public void setHeight(String height) { - setBlockMode(); - super.setHeight(height); - } - - /** - * makes container element (span) to be block element to enable sizing. - */ - private void setBlockMode() { - if (!isBlockMode) { - DOM.setStyleAttribute(getElement(), "display", "block"); - isBlockMode = true; - } - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IContextMenu.java b/src/com/vaadin/terminal/gwt/client/ui/IContextMenu.java deleted file mode 100644 index 5dc2e981b9..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IContextMenu.java +++ /dev/null @@ -1,157 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import com.google.gwt.dom.client.NodeList; -import com.google.gwt.dom.client.TableRowElement; -import com.google.gwt.dom.client.TableSectionElement; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.ui.MenuBar; -import com.google.gwt.user.client.ui.MenuItem; -import com.google.gwt.user.client.ui.PopupPanel; - -public class IContextMenu extends IToolkitOverlay implements SubPartAware { - - private ActionOwner actionOwner; - - private final CMenuBar menu = new CMenuBar(); - - private int left; - - private int top; - - /** - * This method should be used only by Client object as only one per client - * should exists. Request an instance via client.getContextMenu(); - * - * @param cli - * to be set as an owner of menu - */ - public IContextMenu() { - super(true, false, true); - setWidget(menu); - setStyleName("i-contextmenu"); - } - - /** - * Sets the element from which to build menu - * - * @param ao - */ - public void setActionOwner(ActionOwner ao) { - actionOwner = ao; - } - - /** - * Shows context menu at given location. - * - * @param left - * @param top - */ - public void showAt(int left, int top) { - this.left = left; - this.top = top; - menu.clearItems(); - final Action[] actions = actionOwner.getActions(); - for (int i = 0; i < actions.length; i++) { - final Action a = actions[i]; - menu.addItem(new MenuItem(a.getHTML(), true, a)); - } - - setPopupPositionAndShow(new PositionCallback() { - public void setPosition(int offsetWidth, int offsetHeight) { - // mac FF gets bad width due GWT popups overflow hacks, - // re-determine width - offsetWidth = menu.getOffsetWidth(); - int left = IContextMenu.this.left; - int top = IContextMenu.this.top; - if (offsetWidth + left > Window.getClientWidth()) { - left = left - offsetWidth; - if (left < 0) { - left = 0; - } - } - if (offsetHeight + top > Window.getClientHeight()) { - top = top - offsetHeight; - if (top < 0) { - top = 0; - } - } - setPopupPosition(left, top); - } - }); - } - - public void showAt(ActionOwner ao, int left, int top) { - setActionOwner(ao); - showAt(left, top); - } - - /** - * Extend standard Gwt MenuBar to set proper settings and to override - * onPopupClosed method so that PopupPanel gets closed. - */ - class CMenuBar extends MenuBar { - public CMenuBar() { - super(true); - } - - @Override - public void onPopupClosed(PopupPanel sender, boolean autoClosed) { - super.onPopupClosed(sender, autoClosed); - hide(); - } - - /* - * public void onBrowserEvent(Event event) { // Remove current selection - * when mouse leaves if (DOM.eventGetType(event) == Event.ONMOUSEOUT) { - * Element to = DOM.eventGetToElement(event); if - * (!DOM.isOrHasChild(getElement(), to)) { DOM.setElementProperty( - * super.getSelectedItem().getElement(), "className", - * super.getSelectedItem().getStylePrimaryName()); } } - * - * super.onBrowserEvent(event); } - */ - } - - public Element getSubPartElement(String subPart) { - int index = Integer.parseInt(subPart.substring(6)); - // ApplicationConnection.getConsole().log( - // "Searching element for selection index " + index); - Element wrapperdiv = menu.getElement(); - com.google.gwt.dom.client.TableSectionElement tBody = (TableSectionElement) wrapperdiv - .getFirstChildElement().getFirstChildElement(); - TableRowElement item = tBody.getRows().getItem(index); - com.google.gwt.dom.client.Element clickableDivElement = item - .getFirstChildElement().getFirstChildElement(); - return clickableDivElement.cast(); - } - - public String getSubPartName(Element subElement) { - if (getElement().isOrHasChild(subElement)) { - com.google.gwt.dom.client.Element e = subElement; - { - while (e != null && !e.getTagName().toLowerCase().equals("tr")) { - e = e.getParentElement(); - // ApplicationConnection.getConsole().log("Found row"); - } - } - com.google.gwt.dom.client.TableSectionElement parentElement = (TableSectionElement) e - .getParentElement(); - NodeList rows = parentElement.getRows(); - for (int i = 0; i < rows.getLength(); i++) { - if (rows.getItem(i) == e) { - // ApplicationConnection.getConsole().log( - // "Found index for row" + 1); - return "option" + i; - } - } - return null; - } else { - return null; - } - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ICustomComponent.java b/src/com/vaadin/terminal/gwt/client/ui/ICustomComponent.java deleted file mode 100644 index 1e92111efa..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ICustomComponent.java +++ /dev/null @@ -1,153 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.Set; - -import com.google.gwt.user.client.Command; -import com.google.gwt.user.client.DeferredCommand; -import com.google.gwt.user.client.ui.SimplePanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.Container; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.RenderSpace; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; - -public class ICustomComponent extends SimplePanel implements Container { - - private static final String CLASSNAME = "i-customcomponent"; - private String height; - private ApplicationConnection client; - private boolean rendering; - private String width; - private RenderSpace renderSpace = new RenderSpace(); - - public ICustomComponent() { - super(); - setStyleName(CLASSNAME); - } - - public void updateFromUIDL(UIDL uidl, final ApplicationConnection client) { - rendering = true; - if (client.updateComponent(this, uidl, true)) { - rendering = false; - return; - } - this.client = client; - - final UIDL child = uidl.getChildUIDL(0); - if (child != null) { - final Paintable p = client.getPaintable(child); - if (p != getWidget()) { - if (getWidget() != null) { - client.unregisterPaintable((Paintable) getWidget()); - clear(); - } - setWidget((Widget) p); - } - p.updateFromUIDL(child, client); - } - - boolean updateDynamicSize = updateDynamicSize(); - if (updateDynamicSize) { - DeferredCommand.addCommand(new Command() { - public void execute() { - // FIXME deferred relative size update needed to fix some - // scrollbar issues in sampler. This must be the wrong way - // to do it. Might be that some other component is broken. - client.handleComponentRelativeSize(ICustomComponent.this); - - } - }); - } - - renderSpace.setWidth(getElement().getOffsetWidth()); - renderSpace.setHeight(getElement().getOffsetHeight()); - - rendering = false; - } - - private boolean updateDynamicSize() { - boolean updated = false; - if (isDynamicWidth()) { - int childWidth = Util.getRequiredWidth(getWidget()); - getElement().getStyle().setPropertyPx("width", childWidth); - updated = true; - } - if (isDynamicHeight()) { - int childHeight = Util.getRequiredHeight(getWidget()); - getElement().getStyle().setPropertyPx("height", childHeight); - updated = true; - } - - return updated; - } - - private boolean isDynamicWidth() { - return width == null || width.equals(""); - } - - private boolean isDynamicHeight() { - return height == null || height.equals(""); - } - - public boolean hasChildComponent(Widget component) { - if (getWidget() == component) { - return true; - } else { - return false; - } - } - - public void replaceChildComponent(Widget oldComponent, Widget newComponent) { - if (hasChildComponent(oldComponent)) { - clear(); - setWidget(newComponent); - } else { - throw new IllegalStateException(); - } - } - - public void updateCaption(Paintable component, UIDL uidl) { - // NOP, custom component dont render composition roots caption - } - - public boolean requestLayout(Set child) { - return !updateDynamicSize(); - } - - public RenderSpace getAllocatedSpace(Widget child) { - return renderSpace; - } - - @Override - public void setHeight(String height) { - super.setHeight(height); - renderSpace.setHeight(getElement().getOffsetHeight()); - - if (!height.equals(this.height)) { - this.height = height; - if (!rendering) { - client.runDescendentsLayout(this); - } - } - } - - @Override - public void setWidth(String width) { - super.setWidth(width); - renderSpace.setWidth(getElement().getOffsetWidth()); - - if (!width.equals(this.width)) { - this.width = width; - if (!rendering) { - client.runDescendentsLayout(this); - } - } - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ICustomLayout.java b/src/com/vaadin/terminal/gwt/client/ui/ICustomLayout.java deleted file mode 100644 index ac7827f22c..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ICustomLayout.java +++ /dev/null @@ -1,644 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -import com.google.gwt.dom.client.ImageElement; -import com.google.gwt.dom.client.NodeList; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.ComplexPanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.Container; -import com.vaadin.terminal.gwt.client.ContainerResizedListener; -import com.vaadin.terminal.gwt.client.ICaption; -import com.vaadin.terminal.gwt.client.ICaptionWrapper; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.RenderSpace; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; -import com.vaadin.terminal.gwt.client.RenderInformation.FloatSize; - -/** - * Custom Layout implements complex layout defined with HTML template. - * - * @author IT Mill - * - */ -public class ICustomLayout extends ComplexPanel implements Paintable, - Container, ContainerResizedListener { - - public static final String CLASSNAME = "i-customlayout"; - - /** Location-name to containing element in DOM map */ - private final HashMap locationToElement = new HashMap(); - - /** Location-name to contained widget map */ - private final HashMap locationToWidget = new HashMap(); - - /** Widget to captionwrapper map */ - private final HashMap widgetToCaptionWrapper = new HashMap(); - - /** Name of the currently rendered style */ - String currentTemplateName; - - /** Unexecuted scripts loaded from the template */ - private String scripts = ""; - - /** Paintable ID of this paintable */ - private String pid; - - private ApplicationConnection client; - - /** Has the template been loaded from contents passed in UIDL **/ - private boolean hasTemplateContents = false; - - private Element elementWithNativeResizeFunction; - - private String height = ""; - - private String width = ""; - - private HashMap locationToExtraSize = new HashMap(); - - public ICustomLayout() { - setElement(DOM.createDiv()); - // Clear any unwanted styling - DOM.setStyleAttribute(getElement(), "border", "none"); - DOM.setStyleAttribute(getElement(), "margin", "0"); - DOM.setStyleAttribute(getElement(), "padding", "0"); - - if (BrowserInfo.get().isIE()) { - DOM.setStyleAttribute(getElement(), "position", "relative"); - } - - setStyleName(CLASSNAME); - } - - /** - * Sets widget to given location. - * - * If location already contains a widget it will be removed. - * - * @param widget - * Widget to be set into location. - * @param location - * location name where widget will be added - * - * @throws IllegalArgumentException - * if no such location is found in the layout. - */ - public void setWidget(Widget widget, String location) { - - if (widget == null) { - return; - } - - // If no given location is found in the layout, and exception is throws - Element elem = (Element) locationToElement.get(location); - if (elem == null && hasTemplate()) { - throw new IllegalArgumentException("No location " + location - + " found"); - } - - // Get previous widget - final Widget previous = locationToWidget.get(location); - // NOP if given widget already exists in this location - if (previous == widget) { - return; - } - - if (previous != null) { - remove(previous); - } - - // if template is missing add element in order - if (!hasTemplate()) { - elem = getElement(); - } - - // Add widget to location - super.add(widget, elem); - locationToWidget.put(location, widget); - } - - /** Update the layout from UIDL */ - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - this.client = client; - // ApplicationConnection manages generic component features - if (client.updateComponent(this, uidl, true)) { - return; - } - - pid = uidl.getId(); - if (!hasTemplate()) { - // Update HTML template only once - initializeHTML(uidl, client); - } - - // Evaluate scripts - eval(scripts); - scripts = null; - - iLayout(); - // TODO Check if this is needed - client.runDescendentsLayout(this); - - Set oldWidgets = new HashSet(); - oldWidgets.addAll(locationToWidget.values()); - - // For all contained widgets - for (final Iterator i = uidl.getChildIterator(); i.hasNext();) { - final UIDL uidlForChild = (UIDL) i.next(); - if (uidlForChild.getTag().equals("location")) { - final String location = uidlForChild.getStringAttribute("name"); - final Paintable child = client.getPaintable(uidlForChild - .getChildUIDL(0)); - try { - setWidget((Widget) child, location); - child.updateFromUIDL(uidlForChild.getChildUIDL(0), client); - } catch (final IllegalArgumentException e) { - // If no location is found, this component is not visible - } - oldWidgets.remove(child); - } - } - for (Iterator iterator = oldWidgets.iterator(); iterator.hasNext();) { - Widget oldWidget = (Widget) iterator.next(); - if (oldWidget.isAttached()) { - // slot of this widget is emptied, remove it - remove(oldWidget); - } - } - - iLayout(); - // TODO Check if this is needed - client.runDescendentsLayout(this); - - } - - /** Initialize HTML-layout. */ - private void initializeHTML(UIDL uidl, ApplicationConnection client) { - - final String newTemplateContents = uidl - .getStringAttribute("templateContents"); - final String newTemplate = uidl.getStringAttribute("template"); - - currentTemplateName = null; - hasTemplateContents = false; - - String template = ""; - if (newTemplate != null) { - // Get the HTML-template from client - template = client.getResource("layouts/" + newTemplate + ".html"); - if (template == null) { - template = "Layout file layouts/" - + newTemplate - + ".html is missing. Components will be drawn for debug purposes."; - } else { - currentTemplateName = newTemplate; - } - } else { - hasTemplateContents = true; - template = newTemplateContents; - } - - // Connect body of the template to DOM - template = extractBodyAndScriptsFromTemplate(template); - - // TODO prefix img src:s here with a regeps, cannot work further with IE - - String themeUri = client.getThemeUri(); - String relImgPrefix = themeUri + "/layouts/"; - - // prefix all relative image elements to point to theme dir with a - // regexp search - template = template.replaceAll( - "<((?:img)|(?:IMG))\\s([^>]*)src=\"((?![a-z]+:)[^/][^\"]+)\"", - "<$1 $2src=\"" + relImgPrefix + "$3\""); - // also support src attributes without quotes - template = template - .replaceAll( - "<((?:img)|(?:IMG))\\s([^>]*)src=[^\"]((?![a-z]+:)[^/][^ />]+)[ />]", - "<$1 $2src=\"" + relImgPrefix + "$3\""); - // also prefix relative style="...url(...)..." - template = template - .replaceAll( - "(<[^>]+style=\"[^\"]*url\\()((?![a-z]+:)[^/][^\"]+)(\\)[^>]*>)", - "$1 " + relImgPrefix + "$2 $3"); - - getElement().setInnerHTML(template); - - // Remap locations to elements - locationToElement.clear(); - scanForLocations(getElement()); - - initImgElements(); - - elementWithNativeResizeFunction = DOM.getFirstChild(getElement()); - if (elementWithNativeResizeFunction == null) { - elementWithNativeResizeFunction = getElement(); - } - publishResizedFunction(elementWithNativeResizeFunction); - - } - - private native boolean uriEndsWithSlash() - /*-{ - var path = $wnd.location.pathname; - if(path.charAt(path.length - 1) == "/") - return true; - return false; - }-*/; - - private boolean hasTemplate() { - if (currentTemplateName == null && !hasTemplateContents) { - return false; - } else { - return true; - } - } - - /** Collect locations from template */ - private void scanForLocations(Element elem) { - - final String location = elem.getAttribute("location"); - if (!"".equals(location)) { - locationToElement.put(location, elem); - elem.setInnerHTML(""); - int x = Util.measureHorizontalPaddingAndBorder(elem, 0); - int y = Util.measureVerticalPaddingAndBorder(elem, 0); - - FloatSize fs = new FloatSize(x, y); - - locationToExtraSize.put(location, fs); - - } else { - final int len = DOM.getChildCount(elem); - for (int i = 0; i < len; i++) { - scanForLocations(DOM.getChild(elem, i)); - } - } - } - - /** Evaluate given script in browser document */ - private static native void eval(String script) - /*-{ - try { - if (script != null) - eval("{ var document = $doc; var window = $wnd; "+ script + "}"); - } catch (e) { - } - }-*/; - - /** - * Img elements needs some special handling in custom layout. Img elements - * will get their onload events sunk. This way custom layout can notify - * parent about possible size change. - */ - private void initImgElements() { - NodeList nodeList = getElement() - .getElementsByTagName("IMG"); - for (int i = 0; i < nodeList.getLength(); i++) { - com.google.gwt.dom.client.ImageElement img = (ImageElement) nodeList - .getItem(i); - DOM.sinkEvents((Element) img.cast(), Event.ONLOAD); - } - } - - /** - * Extract body part and script tags from raw html-template. - * - * Saves contents of all script-tags to private property: scripts. Returns - * contents of the body part for the html without script-tags. Also replaces - * all _UID_ tags with an unique id-string. - * - * @param html - * Original HTML-template received from server - * @return html that is used to create the HTMLPanel. - */ - private String extractBodyAndScriptsFromTemplate(String html) { - - // Replace UID:s - html = html.replaceAll("_UID_", pid + "__"); - - // Exctract script-tags - scripts = ""; - int endOfPrevScript = 0; - int nextPosToCheck = 0; - String lc = html.toLowerCase(); - String res = ""; - int scriptStart = lc.indexOf(" 0) { - res += html.substring(endOfPrevScript, scriptStart); - scriptStart = lc.indexOf(">", scriptStart); - final int j = lc.indexOf("", scriptStart); - scripts += html.substring(scriptStart + 1, j) + ";"; - nextPosToCheck = endOfPrevScript = j + "".length(); - scriptStart = lc.indexOf("", startOfBody) + 1; - final int endOfBody = lc.indexOf("", startOfBody); - if (endOfBody > startOfBody) { - res = html.substring(startOfBody, endOfBody); - } else { - res = html.substring(startOfBody); - } - } - - return res; - } - - /** Replace child components */ - public void replaceChildComponent(Widget from, Widget to) { - final String location = getLocation(from); - if (location == null) { - throw new IllegalArgumentException(); - } - setWidget(to, location); - } - - /** Does this layout contain given child */ - public boolean hasChildComponent(Widget component) { - return locationToWidget.containsValue(component); - } - - /** Update caption for given widget */ - public void updateCaption(Paintable component, UIDL uidl) { - ICaptionWrapper wrapper = (ICaptionWrapper) widgetToCaptionWrapper - .get(component); - if (ICaption.isNeeded(uidl)) { - if (wrapper == null) { - final String loc = getLocation((Widget) component); - super.remove((Widget) component); - wrapper = new ICaptionWrapper(component, client); - super.add(wrapper, (Element) locationToElement.get(loc)); - widgetToCaptionWrapper.put(component, wrapper); - } - wrapper.updateCaption(uidl); - } else { - if (wrapper != null) { - final String loc = getLocation((Widget) component); - super.remove(wrapper); - super.add((Widget) wrapper.getPaintable(), - (Element) locationToElement.get(loc)); - widgetToCaptionWrapper.remove(component); - } - } - } - - /** Get the location of an widget */ - public String getLocation(Widget w) { - for (final Iterator i = locationToWidget.keySet().iterator(); i - .hasNext();) { - final String location = (String) i.next(); - if (locationToWidget.get(location) == w) { - return location; - } - } - return null; - } - - /** Removes given widget from the layout */ - @Override - public boolean remove(Widget w) { - client.unregisterPaintable((Paintable) w); - final String location = getLocation(w); - if (location != null) { - locationToWidget.remove(location); - } - final ICaptionWrapper cw = (ICaptionWrapper) widgetToCaptionWrapper - .get(w); - if (cw != null) { - widgetToCaptionWrapper.remove(w); - return super.remove(cw); - } else if (w != null) { - return super.remove(w); - } - return false; - } - - /** Adding widget without specifying location is not supported */ - @Override - public void add(Widget w) { - throw new UnsupportedOperationException(); - } - - /** Clear all widgets from the layout */ - @Override - public void clear() { - super.clear(); - locationToWidget.clear(); - widgetToCaptionWrapper.clear(); - } - - public void iLayout() { - iLayoutJS(DOM.getFirstChild(getElement())); - } - - /** - * This method is published to JS side with the same name into first DOM - * node of custom layout. This way if one implements some resizeable - * containers in custom layout he/she can notify children after resize. - */ - public void notifyChildrenOfSizeChange() { - client.runDescendentsLayout(this); - } - - @Override - public void onDetach() { - super.onDetach(); - detachResizedFunction(elementWithNativeResizeFunction); - } - - private native void detachResizedFunction(Element element) - /*-{ - element.notifyChildrenOfSizeChange = null; - }-*/; - - private native void publishResizedFunction(Element element) - /*-{ - var self = this; - element.notifyChildrenOfSizeChange = function() { - self.@com.vaadin.terminal.gwt.client.ui.ICustomLayout::notifyChildrenOfSizeChange()(); - }; - }-*/; - - /** - * In custom layout one may want to run layout functions made with - * JavaScript. This function tests if one exists (with name "iLayoutJS" in - * layouts first DOM node) and runs et. Return value is used to determine if - * children needs to be notified of size changes. - * - * Note! When implementing a JS layout function you most likely want to call - * notifyChildrenOfSizeChange() function on your custom layouts main - * element. That method is used to control whether child components layout - * functions are to be run. - * - * @param el - * @return true if layout function exists and was run successfully, else - * false. - */ - private native boolean iLayoutJS(Element el) - /*-{ - if(el && el.iLayoutJS) { - try { - el.iLayoutJS(); - return true; - } catch (e) { - return false; - } - } else { - return false; - } - }-*/; - - public boolean requestLayout(Set child) { - updateRelativeSizedComponents(true, true); - - if (width.equals("") || height.equals("")) { - /* Automatically propagated upwards if the size can change */ - return false; - } - - return true; - } - - public RenderSpace getAllocatedSpace(Widget child) { - com.google.gwt.dom.client.Element pe = child.getElement() - .getParentElement(); - - FloatSize extra = locationToExtraSize.get(getLocation(child)); - return new RenderSpace(pe.getOffsetWidth() - (int) extra.getWidth(), pe - .getOffsetHeight() - - (int) extra.getHeight(), Util.mayHaveScrollBars(pe)); - } - - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - if (event.getTypeInt() == Event.ONLOAD) { - Util.notifyParentOfSizeChange(this, true); - event.cancelBubble(true); - } - } - - @Override - public void setHeight(String height) { - if (this.height.equals(height)) { - return; - } - - boolean shrinking = true; - if (isLarger(height, this.height)) { - shrinking = false; - } - - this.height = height; - super.setHeight(height); - - /* - * If the height shrinks we must remove all components with relative - * height from the DOM, update their height when they do not affect the - * available space and finally restore them to the original state - */ - if (shrinking) { - updateRelativeSizedComponents(false, true); - } - } - - @Override - public void setWidth(String width) { - if (this.width.equals(width)) { - return; - } - - boolean shrinking = true; - if (isLarger(width, this.width)) { - shrinking = false; - } - - super.setWidth(width); - this.width = width; - - /* - * If the width shrinks we must remove all components with relative - * width from the DOM, update their width when they do not affect the - * available space and finally restore them to the original state - */ - if (shrinking) { - updateRelativeSizedComponents(true, false); - } - } - - private void updateRelativeSizedComponents(boolean relativeWidth, - boolean relativeHeight) { - - Set relativeSizeWidgets = new HashSet(); - - for (Widget widget : locationToWidget.values()) { - FloatSize relativeSize = client.getRelativeSize(widget); - if (relativeSize != null) { - if ((relativeWidth && (relativeSize.getWidth() >= 0.0f)) - || (relativeHeight && (relativeSize.getHeight() >= 0.0f))) { - - relativeSizeWidgets.add(widget); - widget.getElement().getStyle().setProperty("position", - "absolute"); - } - } - } - - for (Widget widget : relativeSizeWidgets) { - client.handleComponentRelativeSize(widget); - widget.getElement().getStyle().setProperty("position", ""); - } - } - - /** - * Compares newSize with currentSize and returns true if it is clear that - * newSize is larger than currentSize. Returns false if newSize is smaller - * or if it is unclear which one is smaller. - * - * @param newSize - * @param currentSize - * @return - */ - private boolean isLarger(String newSize, String currentSize) { - if (newSize.equals("") || currentSize.equals("")) { - return false; - } - - if (!newSize.endsWith("px") || !currentSize.endsWith("px")) { - return false; - } - - int newSizePx = Integer.parseInt(newSize.substring(0, - newSize.length() - 2)); - int currentSizePx = Integer.parseInt(currentSize.substring(0, - currentSize.length() - 2)); - - boolean larger = newSizePx > currentSizePx; - return larger; - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IDateField.java b/src/com/vaadin/terminal/gwt/client/ui/IDateField.java deleted file mode 100644 index b62def236e..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IDateField.java +++ /dev/null @@ -1,233 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.Date; - -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.FlowPanel; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.ClientExceptionHandler; -import com.vaadin.terminal.gwt.client.DateTimeService; -import com.vaadin.terminal.gwt.client.ITooltip; -import com.vaadin.terminal.gwt.client.LocaleNotLoadedException; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; - -public class IDateField extends FlowPanel implements Paintable, Field { - - public static final String CLASSNAME = "i-datefield"; - - protected String id; - - protected ApplicationConnection client; - - protected boolean immediate; - - public static final int RESOLUTION_YEAR = 0; - public static final int RESOLUTION_MONTH = 1; - public static final int RESOLUTION_DAY = 2; - public static final int RESOLUTION_HOUR = 3; - public static final int RESOLUTION_MIN = 4; - public static final int RESOLUTION_SEC = 5; - public static final int RESOLUTION_MSEC = 6; - - protected int currentResolution = RESOLUTION_YEAR; - - protected String currentLocale; - - protected boolean readonly; - - protected boolean enabled; - - protected Date date = null; - // e.g when paging a calendar, before actually selecting - protected Date showingDate = new Date(); - - protected DateTimeService dts; - - public IDateField() { - setStyleName(CLASSNAME); - dts = new DateTimeService(); - sinkEvents(ITooltip.TOOLTIP_EVENTS); - } - - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - if (client != null) { - client.handleTooltipEvent(event, this); - } - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - // Ensure correct implementation and let layout manage caption - if (client.updateComponent(this, uidl, true)) { - return; - } - - // Save details - this.client = client; - id = uidl.getId(); - immediate = uidl.getBooleanAttribute("immediate"); - - readonly = uidl.getBooleanAttribute("readonly"); - enabled = !uidl.getBooleanAttribute("disabled"); - - if (uidl.hasAttribute("locale")) { - final String locale = uidl.getStringAttribute("locale"); - try { - dts.setLocale(locale); - currentLocale = locale; - } catch (final LocaleNotLoadedException e) { - currentLocale = dts.getLocale(); - ClientExceptionHandler.displayError( - "Tried to use an unloaded locale \"" + locale - + "\". Using default locale (" + currentLocale - + ").", e); - } - } - - int newResolution; - if (uidl.hasVariable("msec")) { - newResolution = RESOLUTION_MSEC; - } else if (uidl.hasVariable("sec")) { - newResolution = RESOLUTION_SEC; - } else if (uidl.hasVariable("min")) { - newResolution = RESOLUTION_MIN; - } else if (uidl.hasVariable("hour")) { - newResolution = RESOLUTION_HOUR; - } else if (uidl.hasVariable("day")) { - newResolution = RESOLUTION_DAY; - } else if (uidl.hasVariable("month")) { - newResolution = RESOLUTION_MONTH; - } else { - newResolution = RESOLUTION_YEAR; - } - - currentResolution = newResolution; - - final int year = uidl.getIntVariable("year"); - final int month = (currentResolution >= RESOLUTION_MONTH) ? uidl - .getIntVariable("month") : -1; - final int day = (currentResolution >= RESOLUTION_DAY) ? uidl - .getIntVariable("day") : -1; - final int hour = (currentResolution >= RESOLUTION_HOUR) ? uidl - .getIntVariable("hour") : 0; - final int min = (currentResolution >= RESOLUTION_MIN) ? uidl - .getIntVariable("min") : 0; - final int sec = (currentResolution >= RESOLUTION_SEC) ? uidl - .getIntVariable("sec") : 0; - final int msec = (currentResolution >= RESOLUTION_MSEC) ? uidl - .getIntVariable("msec") : 0; - - // Construct new date for this datefield (only if not null) - if (year > -1) { - date = new Date((long) getTime(year, month, day, hour, min, sec, - msec)); - showingDate.setTime(date.getTime()); - } else { - date = null; - showingDate = new Date(); - } - - } - - /* - * We need this redundant native function because Java's Date object doesn't - * have a setMilliseconds method. - */ - private static native double getTime(int y, int m, int d, int h, int mi, - int s, int ms) - /*-{ - try { - var date = new Date(2000,1,1,1); // don't use current date here - if(y && y >= 0) date.setFullYear(y); - if(m && m >= 1) date.setMonth(m-1); - if(d && d >= 0) date.setDate(d); - if(h >= 0) date.setHours(h); - if(mi >= 0) date.setMinutes(mi); - if(s >= 0) date.setSeconds(s); - if(ms >= 0) date.setMilliseconds(ms); - return date.getTime(); - } catch (e) { - // TODO print some error message on the console - //console.log(e); - return (new Date()).getTime(); - } - }-*/; - - public int getMilliseconds() { - return (int) (date.getTime() - date.getTime() / 1000 * 1000); - } - - public void setMilliseconds(int ms) { - date.setTime(date.getTime() / 1000 * 1000 + ms); - } - - public int getShowingMilliseconds() { - return (int) (showingDate.getTime() - showingDate.getTime() / 1000 * 1000); - } - - public void setShowingMilliseconds(int ms) { - showingDate.setTime(showingDate.getTime() / 1000 * 1000 + ms); - } - - public int getCurrentResolution() { - return currentResolution; - } - - public void setCurrentResolution(int currentResolution) { - this.currentResolution = currentResolution; - } - - public String getCurrentLocale() { - return currentLocale; - } - - public void setCurrentLocale(String currentLocale) { - this.currentLocale = currentLocale; - } - - public Date getCurrentDate() { - return date; - } - - public void setCurrentDate(Date date) { - this.date = date; - } - - public Date getShowingDate() { - return showingDate; - } - - public void setShowingDate(Date date) { - showingDate = date; - } - - public boolean isImmediate() { - return immediate; - } - - public boolean isReadonly() { - return readonly; - } - - public boolean isEnabled() { - return enabled; - } - - public DateTimeService getDateTimeService() { - return dts; - } - - public String getId() { - return id; - } - - public ApplicationConnection getClient() { - return client; - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IDateFieldCalendar.java b/src/com/vaadin/terminal/gwt/client/ui/IDateFieldCalendar.java deleted file mode 100644 index 24f614702e..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IDateFieldCalendar.java +++ /dev/null @@ -1,26 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.UIDL; - -public class IDateFieldCalendar extends IDateField { - - private final ICalendarPanel date; - - public IDateFieldCalendar() { - super(); - date = new ICalendarPanel(this); - add(date); - } - - @Override - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - super.updateFromUIDL(uidl, client); - date.updateCalendar(); - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IEmbedded.java b/src/com/vaadin/terminal/gwt/client/ui/IEmbedded.java deleted file mode 100644 index 8c8340b470..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IEmbedded.java +++ /dev/null @@ -1,225 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.Iterator; - -import com.google.gwt.dom.client.Document; -import com.google.gwt.dom.client.Node; -import com.google.gwt.dom.client.NodeList; -import com.google.gwt.dom.client.ObjectElement; -import com.google.gwt.dom.client.Style; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.HTML; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; - -public class IEmbedded extends HTML implements Paintable { - private static String CLASSNAME = "i-embedded"; - - private String height; - private String width; - private Element browserElement; - - private ApplicationConnection client; - - public IEmbedded() { - setStyleName(CLASSNAME); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - if (client.updateComponent(this, uidl, true)) { - return; - } - this.client = client; - - boolean clearBrowserElement = true; - - if (uidl.hasAttribute("type")) { - final String type = uidl.getStringAttribute("type"); - if (type.equals("image")) { - Element el = null; - boolean created = false; - NodeList nodes = getElement().getChildNodes(); - if (nodes != null && nodes.getLength() == 1) { - Node n = nodes.getItem(0); - if (n.getNodeType() == Node.ELEMENT_NODE) { - Element e = (Element) n; - if (e.getTagName().equals("IMG")) { - el = e; - } - } - } - if (el == null) { - setHTML(""); - el = DOM.createImg(); - created = true; - client.addPngFix(el); - DOM.sinkEvents(el, Event.ONLOAD); - } - - // Set attributes - Style style = el.getStyle(); - String w = uidl.getStringAttribute("width"); - if (w != null) { - style.setProperty("width", w); - } else { - style.setProperty("width", ""); - } - String h = uidl.getStringAttribute("height"); - if (h != null) { - style.setProperty("height", h); - } else { - style.setProperty("height", ""); - } - DOM.setElementProperty(el, "src", getSrc(uidl, client)); - - if (created) { - // insert in dom late - getElement().appendChild(el); - } - - } else if (type.equals("browser")) { - if (browserElement == null) { - setHTML(""); - browserElement = DOM.getFirstChild(getElement()); - } else { - DOM.setElementAttribute(browserElement, "src", getSrc(uidl, - client)); - } - clearBrowserElement = false; - } else { - ApplicationConnection.getConsole().log( - "Unknown Embedded type '" + type + "'"); - } - } else if (uidl.hasAttribute("mimetype")) { - final String mime = uidl.getStringAttribute("mimetype"); - if (mime.equals("application/x-shockwave-flash")) { - setHTML(""); - } else if (mime.equals("image/svg+xml")) { - String data; - if (getParameter("data", uidl) == null) { - data = getSrc(uidl, client); - } else { - data = "data:image/svg+xml," + getParameter("data", uidl); - } - setHTML(""); - ObjectElement obj = Document.get().createObjectElement(); - obj.setType(mime); - obj.setData(data); - if (width != null) { - obj.getStyle().setProperty("width", "100%"); - } - if (height != null) { - obj.getStyle().setProperty("height", "100%"); - } - getElement().appendChild(obj); - - } else { - ApplicationConnection.getConsole().log( - "Unknown Embedded mimetype '" + mime + "'"); - } - } else { - ApplicationConnection.getConsole().log( - "Unknown Embedded; no type or mimetype attribute"); - } - - if (clearBrowserElement) { - browserElement = null; - } - - } - - private static String getParameter(String paramName, UIDL uidl) { - Iterator childIterator = uidl.getChildIterator(); - while (childIterator.hasNext()) { - Object child = childIterator.next(); - if (child instanceof UIDL) { - UIDL childUIDL = (UIDL) child; - if (childUIDL.getTag().equals("embeddedparam") - && childUIDL.getStringAttribute("name").equals( - paramName)) { - return childUIDL.getStringAttribute("value"); - } - - } - } - return null; - } - - /** - * Helper to return translated src-attribute from embedded's UIDL - * - * @param uidl - * @param client - * @return - */ - private String getSrc(UIDL uidl, ApplicationConnection client) { - String url = client.translateToolkitUri(uidl.getStringAttribute("src")); - if (url == null) { - return ""; - } - return url; - } - - @Override - public void setWidth(String width) { - this.width = width; - if (isDynamicHeight()) { - int oldHeight = getOffsetHeight(); - super.setWidth(width); - int newHeight = getOffsetHeight(); - /* - * Must notify parent if the height changes as a result of a width - * change - */ - if (oldHeight != newHeight) { - Util.notifyParentOfSizeChange(this, false); - } - } else { - super.setWidth(width); - } - - } - - private boolean isDynamicHeight() { - return height == null || height.equals(""); - } - - @Override - public void setHeight(String height) { - this.height = height; - super.setHeight(height); - } - - @Override - protected void onDetach() { - // Force browser to fire unload event when component is detached from - // the view (IE doesn't do this automatically) - if (browserElement != null) { - DOM.setElementAttribute(browserElement, "src", "javascript:false"); - } - super.onDetach(); - } - - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - if (DOM.eventGetType(event) == Event.ONLOAD) { - Util.notifyParentOfSizeChange(this, true); - } - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IFilterSelect.java b/src/com/vaadin/terminal/gwt/client/ui/IFilterSelect.java deleted file mode 100644 index ec9ecf1cf6..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IFilterSelect.java +++ /dev/null @@ -1,1059 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.Iterator; -import java.util.List; - -import com.google.gwt.user.client.Command; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.ui.ClickListener; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.FocusListener; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.Image; -import com.google.gwt.user.client.ui.KeyboardListener; -import com.google.gwt.user.client.ui.LoadListener; -import com.google.gwt.user.client.ui.PopupListener; -import com.google.gwt.user.client.ui.PopupPanel; -import com.google.gwt.user.client.ui.TextBox; -import com.google.gwt.user.client.ui.Widget; -import com.google.gwt.user.client.ui.PopupPanel.PositionCallback; -import com.google.gwt.user.client.ui.SuggestOracle.Suggestion; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.Focusable; -import com.vaadin.terminal.gwt.client.ITooltip; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; - -/** - * - * TODO needs major refactoring (to be extensible etc) - */ -public class IFilterSelect extends Composite implements Paintable, Field, - KeyboardListener, ClickListener, FocusListener, Focusable { - - public class FilterSelectSuggestion implements Suggestion, Command { - - private final String key; - private final String caption; - private String iconUri; - - public FilterSelectSuggestion(UIDL uidl) { - key = uidl.getStringAttribute("key"); - caption = uidl.getStringAttribute("caption"); - if (uidl.hasAttribute("icon")) { - iconUri = client.translateToolkitUri(uidl - .getStringAttribute("icon")); - } - } - - public String getDisplayString() { - final StringBuffer sb = new StringBuffer(); - if (iconUri != null) { - sb.append("\"\""); - } - sb.append("" + Util.escapeHTML(caption) + ""); - return sb.toString(); - } - - public String getReplacementString() { - return caption; - } - - public int getOptionKey() { - return Integer.parseInt(key); - } - - public String getIconUri() { - return iconUri; - } - - public void execute() { - onSuggestionSelected(this); - } - } - - public class SuggestionPopup extends IToolkitOverlay implements - PositionCallback, PopupListener { - - private static final String Z_INDEX = "30000"; - - private final SuggestionMenu menu; - - private final Element up = DOM.createDiv(); - private final Element down = DOM.createDiv(); - private final Element status = DOM.createDiv(); - - private boolean isPagingEnabled = true; - - private long lastAutoClosed; - - private int popupOuterPadding = -1; - - private int topPosition; - - SuggestionPopup() { - super(true, false, true); - menu = new SuggestionMenu(); - setWidget(menu); - setStyleName(CLASSNAME + "-suggestpopup"); - DOM.setStyleAttribute(getElement(), "zIndex", Z_INDEX); - - final Element root = getContainerElement(); - - DOM.setInnerHTML(up, "Prev"); - DOM.sinkEvents(up, Event.ONCLICK); - DOM.setInnerHTML(down, "Next"); - DOM.sinkEvents(down, Event.ONCLICK); - DOM.insertChild(root, up, 0); - DOM.appendChild(root, down); - DOM.appendChild(root, status); - DOM.setElementProperty(status, "className", CLASSNAME + "-status"); - - addPopupListener(this); - } - - public void showSuggestions( - Collection currentSuggestions, - int currentPage, int totalSuggestions) { - - // Add TT anchor point - DOM.setElementProperty(getElement(), "id", - "TOOLKIT_COMBOBOX_OPTIONLIST"); - - menu.setSuggestions(currentSuggestions); - final int x = IFilterSelect.this.getAbsoluteLeft(); - topPosition = tb.getAbsoluteTop(); - topPosition += tb.getOffsetHeight(); - setPopupPosition(x, topPosition); - - final int first = currentPage * PAGELENTH - + (nullSelectionAllowed && currentPage > 0 ? 0 : 1); - final int last = first + currentSuggestions.size() - 1; - final int matches = totalSuggestions - - (nullSelectionAllowed ? 1 : 0); - if (last > 0) { - // nullsel not counted, as requested by user - DOM.setInnerText(status, (matches == 0 ? 0 : first) - + "-" - + ("".equals(lastFilter) && nullSelectionAllowed - && currentPage == 0 ? last - 1 : last) + "/" - + matches); - } else { - DOM.setInnerText(status, ""); - } - // We don't need to show arrows or statusbar if there is only one - // page - if (matches <= PAGELENTH) { - setPagingEnabled(false); - } else { - setPagingEnabled(true); - } - setPrevButtonActive(first > 1); - setNextButtonActive(last < matches); - - // clear previously fixed width - menu.setWidth(""); - DOM.setStyleAttribute(DOM.getFirstChild(menu.getElement()), - "width", ""); - - setPopupPositionAndShow(this); - } - - private void setNextButtonActive(boolean b) { - if (b) { - DOM.sinkEvents(down, Event.ONCLICK); - DOM.setElementProperty(down, "className", CLASSNAME - + "-nextpage"); - } else { - DOM.sinkEvents(down, 0); - DOM.setElementProperty(down, "className", CLASSNAME - + "-nextpage-off"); - } - } - - private void setPrevButtonActive(boolean b) { - if (b) { - DOM.sinkEvents(up, Event.ONCLICK); - DOM - .setElementProperty(up, "className", CLASSNAME - + "-prevpage"); - } else { - DOM.sinkEvents(up, 0); - DOM.setElementProperty(up, "className", CLASSNAME - + "-prevpage-off"); - } - - } - - public void selectNextItem() { - final MenuItem cur = menu.getSelectedItem(); - final int index = 1 + menu.getItems().indexOf(cur); - if (menu.getItems().size() > index) { - final MenuItem newSelectedItem = (MenuItem) menu.getItems() - .get(index); - menu.selectItem(newSelectedItem); - tb.setText(newSelectedItem.getText()); - tb.setSelectionRange(lastFilter.length(), newSelectedItem - .getText().length() - - lastFilter.length()); - - } else if (hasNextPage()) { - lastIndex = index - 1; // save for paging - filterOptions(currentPage + 1, lastFilter); - } - } - - public void selectPrevItem() { - final MenuItem cur = menu.getSelectedItem(); - final int index = -1 + menu.getItems().indexOf(cur); - if (index > -1) { - final MenuItem newSelectedItem = (MenuItem) menu.getItems() - .get(index); - menu.selectItem(newSelectedItem); - tb.setText(newSelectedItem.getText()); - tb.setSelectionRange(lastFilter.length(), newSelectedItem - .getText().length() - - lastFilter.length()); - } else if (index == -1) { - if (currentPage > 0) { - lastIndex = index + 1; // save for paging - filterOptions(currentPage - 1, lastFilter); - } - } else { - final MenuItem newSelectedItem = (MenuItem) menu.getItems() - .get(menu.getItems().size() - 1); - menu.selectItem(newSelectedItem); - tb.setText(newSelectedItem.getText()); - tb.setSelectionRange(lastFilter.length(), newSelectedItem - .getText().length() - - lastFilter.length()); - } - } - - @Override - public void onBrowserEvent(Event event) { - final Element target = DOM.eventGetTarget(event); - if (DOM.compare(target, up) - || DOM.compare(target, DOM.getChild(up, 0))) { - filterOptions(currentPage - 1, lastFilter); - } else if (DOM.compare(target, down) - || DOM.compare(target, DOM.getChild(down, 0))) { - filterOptions(currentPage + 1, lastFilter); - } - tb.setFocus(true); - } - - public void setPagingEnabled(boolean paging) { - if (isPagingEnabled == paging) { - return; - } - if (paging) { - DOM.setStyleAttribute(down, "display", ""); - DOM.setStyleAttribute(up, "display", ""); - DOM.setStyleAttribute(status, "display", ""); - } else { - DOM.setStyleAttribute(down, "display", "none"); - DOM.setStyleAttribute(up, "display", "none"); - DOM.setStyleAttribute(status, "display", "none"); - } - isPagingEnabled = paging; - } - - /* - * (non-Javadoc) - * - * @see - * com.google.gwt.user.client.ui.PopupPanel$PositionCallback#setPosition - * (int, int) - */ - public void setPosition(int offsetWidth, int offsetHeight) { - - int top = -1; - int left = -1; - - // reset menu size and retrieve its "natural" size - menu.setHeight(""); - if (currentPage > 0) { - // fix height to avoid height change when getting to last page - menu.fixHeightTo(PAGELENTH); - } - offsetHeight = getOffsetHeight(); - - final int desiredWidth = getMainWidth(); - int naturalMenuWidth = DOM.getElementPropertyInt(DOM - .getFirstChild(menu.getElement()), "offsetWidth"); - - if (popupOuterPadding == -1) { - popupOuterPadding = Util.measureHorizontalPaddingAndBorder( - getElement(), 2); - } - - if (naturalMenuWidth < desiredWidth) { - menu.setWidth((desiredWidth - popupOuterPadding) + "px"); - DOM.setStyleAttribute(DOM.getFirstChild(menu.getElement()), - "width", "100%"); - naturalMenuWidth = desiredWidth; - } - - if (BrowserInfo.get().isIE()) { - /* - * IE requires us to specify the width for the container - * element. Otherwise it will be 100% wide - */ - int rootWidth = naturalMenuWidth - popupOuterPadding; - DOM.setStyleAttribute(getContainerElement(), "width", rootWidth - + "px"); - } - - if (offsetHeight + getPopupTop() > Window.getClientHeight() - + Window.getScrollTop()) { - // popup on top of input instead - top = getPopupTop() - offsetHeight - - IFilterSelect.this.getOffsetHeight(); - if (top < 0) { - top = 0; - } - } else { - top = getPopupTop(); - /* - * Take popup top margin into account. getPopupTop() returns the - * top value including the margin but the value we give must not - * include the margin. - */ - int topMargin = (top - topPosition); - top -= topMargin; - } - - // fetch real width (mac FF bugs here due GWT popups overflow:auto ) - offsetWidth = DOM.getElementPropertyInt(DOM.getFirstChild(menu - .getElement()), "offsetWidth"); - if (offsetWidth + getPopupLeft() > Window.getClientWidth() - + Window.getScrollLeft()) { - left = IFilterSelect.this.getAbsoluteLeft() - + IFilterSelect.this.getOffsetWidth() - + Window.getScrollLeft() - offsetWidth; - if (left < 0) { - left = 0; - } - } else { - left = getPopupLeft(); - } - setPopupPosition(left, top); - - } - - /** - * @return true if popup was just closed - */ - public boolean isJustClosed() { - final long now = (new Date()).getTime(); - return (lastAutoClosed > 0 && (now - lastAutoClosed) < 200); - } - - public void onPopupClosed(PopupPanel sender, boolean autoClosed) { - if (autoClosed) { - lastAutoClosed = (new Date()).getTime(); - } - } - - /** - * Updates style names in suggestion popup to help theme building. - */ - public void updateStyleNames(UIDL uidl) { - if (uidl.hasAttribute("style")) { - setStyleName(CLASSNAME + "-suggestpopup"); - final String[] styles = uidl.getStringAttribute("style").split( - " "); - for (int i = 0; i < styles.length; i++) { - addStyleDependentName(styles[i]); - } - } - } - - } - - public class SuggestionMenu extends MenuBar { - - SuggestionMenu() { - super(true); - setStyleName(CLASSNAME + "-suggestmenu"); - } - - /** - * Fixes menus height to use same space as full page would use. Needed - * to avoid height changes when quickly "scrolling" to last page - */ - public void fixHeightTo(int pagelenth) { - if (currentSuggestions.size() > 0) { - final int pixels = pagelenth * (getOffsetHeight() - 2) - / currentSuggestions.size(); - setHeight((pixels + 2) + "px"); - } - } - - public void setSuggestions( - Collection suggestions) { - clearItems(); - final Iterator it = suggestions.iterator(); - while (it.hasNext()) { - final FilterSelectSuggestion s = it.next(); - final MenuItem mi = new MenuItem(s.getDisplayString(), true, s); - - com.google.gwt.dom.client.Element child = mi.getElement() - .getFirstChildElement(); - while (child != null) { - if (child.getNodeName().toLowerCase().equals("img")) { - DOM - .sinkEvents((Element) child.cast(), - (DOM.getEventsSunk((Element) child - .cast()) | Event.ONLOAD)); - } - child = child.getNextSiblingElement(); - } - - this.addItem(mi); - if (s == currentSuggestion) { - selectItem(mi); - } - } - } - - public void doSelectedItemAction() { - final MenuItem item = getSelectedItem(); - final String enteredItemValue = tb.getText(); - // check for exact match in menu - int p = getItems().size(); - if (p > 0) { - for (int i = 0; i < p; i++) { - final MenuItem potentialExactMatch = (MenuItem) getItems() - .get(i); - if (potentialExactMatch.getText().equals(enteredItemValue)) { - selectItem(potentialExactMatch); - doItemAction(potentialExactMatch, true); - suggestionPopup.hide(); - return; - } - } - } - if (allowNewItem) { - - if (!prompting && !enteredItemValue.equals(lastNewItemString)) { - /* - * Store last sent new item string to avoid double sends - */ - lastNewItemString = enteredItemValue; - client.updateVariable(paintableId, "newitem", - enteredItemValue, immediate); - } - } else if (item != null - && !"".equals(lastFilter) - && item.getText().toLowerCase().startsWith( - lastFilter.toLowerCase())) { - doItemAction(item, true); - } else { - if (currentSuggestion != null) { - String text = currentSuggestion.getReplacementString(); - /* TODO? - if (text.equals("")) { - addStyleDependentName(CLASSNAME_PROMPT); - tb.setText(inputPrompt); - prompting = true; - } else { - tb.setText(text); - prompting = false; - removeStyleDependentName(CLASSNAME_PROMPT); - } - */ - selectedOptionKey = currentSuggestion.key; - } - } - suggestionPopup.hide(); - } - - @Override - public void onBrowserEvent(Event event) { - if (event.getTypeInt() == Event.ONLOAD) { - if (suggestionPopup.isVisible()) { - setWidth(""); - DOM.setStyleAttribute(DOM.getFirstChild(getElement()), - "width", ""); - suggestionPopup.setPopupPositionAndShow(suggestionPopup); - } - } - super.onBrowserEvent(event); - } - } - - public static final int FILTERINGMODE_OFF = 0; - public static final int FILTERINGMODE_STARTSWITH = 1; - public static final int FILTERINGMODE_CONTAINS = 2; - - private static final String CLASSNAME = "i-filterselect"; - - public static final int PAGELENTH = 10; - - private final FlowPanel panel = new FlowPanel(); - - private final TextBox tb = new TextBox() { - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - if (client != null) { - client.handleTooltipEvent(event, IFilterSelect.this); - } - } - }; - - private final SuggestionPopup suggestionPopup = new SuggestionPopup(); - - private final HTML popupOpener = new HTML(""); - - private final Image selectedItemIcon = new Image(); - - private ApplicationConnection client; - - private String paintableId; - - private int currentPage; - - private final Collection currentSuggestions = new ArrayList(); - - private boolean immediate; - - private String selectedOptionKey; - - private boolean filtering = false; - - private String lastFilter = ""; - private int lastIndex = -1; // last selected index when using arrows - - private FilterSelectSuggestion currentSuggestion; - - private int totalMatches; - private boolean allowNewItem; - private boolean nullSelectionAllowed; - private boolean enabled; - - // shown in unfocused empty field, disappears on focus (e.g "Search here") - private static final String CLASSNAME_PROMPT = "prompt"; - private static final String ATTR_INPUTPROMPT = "prompt"; - private String inputPrompt = ""; - private boolean prompting = false; - - // Set true when popupopened has been clicked. Cleared on each UIDL-update. - // This handles the special case where are not filtering yet and the - // selected value has changed on the server-side. See #2119 - private boolean popupOpenerClicked; - private String width = null; - private int textboxPadding = -1; - private int componentPadding = -1; - private int suggestionPopupMinWidth = 0; - /* - * Stores the last new item string to avoid double submissions. Cleared on - * uidl updates - */ - private String lastNewItemString; - private boolean focused = false; - - public IFilterSelect() { - selectedItemIcon.setVisible(false); - selectedItemIcon.setStyleName("i-icon"); - selectedItemIcon.addLoadListener(new LoadListener() { - public void onError(Widget sender) { - } - - public void onLoad(Widget sender) { - updateRootWidth(); - updateSelectedIconPosition(); - } - }); - - panel.add(selectedItemIcon); - tb.sinkEvents(ITooltip.TOOLTIP_EVENTS); - panel.add(tb); - panel.add(popupOpener); - initWidget(panel); - setStyleName(CLASSNAME); - tb.addKeyboardListener(this); - tb.setStyleName(CLASSNAME + "-input"); - tb.addFocusListener(this); - popupOpener.setStyleName(CLASSNAME + "-button"); - popupOpener.addClickListener(this); - } - - public boolean hasNextPage() { - if (totalMatches > (currentPage + 1) * PAGELENTH) { - return true; - } else { - return false; - } - } - - public void filterOptions(int page) { - filterOptions(page, tb.getText()); - } - - public void filterOptions(int page, String filter) { - if (filter.equals(lastFilter) && currentPage == page) { - if (!suggestionPopup.isAttached()) { - suggestionPopup.showSuggestions(currentSuggestions, - currentPage, totalMatches); - } - return; - } - if (!filter.equals(lastFilter)) { - // we are on subsequent page and text has changed -> reset page - if ("".equals(filter)) { - // let server decide - page = -1; - } else { - page = 0; - } - } - - filtering = true; - client.updateVariable(paintableId, "filter", filter, false); - client.updateVariable(paintableId, "page", page, true); - lastFilter = filter; - currentPage = page; - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - paintableId = uidl.getId(); - this.client = client; - - boolean readonly = uidl.hasAttribute("readonly"); - boolean disabled = uidl.hasAttribute("disabled"); - - if (disabled || readonly) { - tb.setEnabled(false); - enabled = false; - } else { - tb.setEnabled(true); - enabled = true; - } - - if (client.updateComponent(this, uidl, true)) { - return; - } - - // not a FocusWidget -> needs own tabindex handling - if (uidl.hasAttribute("tabindex")) { - tb.setTabIndex(uidl.getIntAttribute("tabindex")); - } - - immediate = uidl.hasAttribute("immediate"); - - nullSelectionAllowed = uidl.hasAttribute("nullselect"); - - currentPage = uidl.getIntVariable("page"); - - if (uidl.hasAttribute(ATTR_INPUTPROMPT)) { - // input prompt changed from server - inputPrompt = uidl.getStringAttribute(ATTR_INPUTPROMPT); - } else { - inputPrompt = ""; - } - - suggestionPopup.setPagingEnabled(true); - suggestionPopup.updateStyleNames(uidl); - - allowNewItem = uidl.hasAttribute("allownewitem"); - lastNewItemString = null; - - currentSuggestions.clear(); - final UIDL options = uidl.getChildUIDL(0); - totalMatches = uidl.getIntAttribute("totalMatches"); - - String captions = inputPrompt; - - for (final Iterator i = options.getChildIterator(); i.hasNext();) { - final UIDL optionUidl = (UIDL) i.next(); - final FilterSelectSuggestion suggestion = new FilterSelectSuggestion( - optionUidl); - currentSuggestions.add(suggestion); - if (optionUidl.hasAttribute("selected")) { - if (!filtering || popupOpenerClicked) { - tb.setText(suggestion.getReplacementString()); - selectedOptionKey = "" + suggestion.getOptionKey(); - } - currentSuggestion = suggestion; - setSelectedItemIcon(suggestion.getIconUri()); - } - - // Collect captions so we can calculate minimum width for textarea - if (captions.length() > 0) { - captions += "|"; - } - captions += suggestion.getReplacementString(); - } - - if ((!filtering || popupOpenerClicked) && uidl.hasVariable("selected") - && uidl.getStringArrayVariable("selected").length == 0) { - // select nulled - if (!filtering || !popupOpenerClicked) { - setPromptingOn(); - } - selectedOptionKey = null; - } - - if (filtering - && lastFilter.toLowerCase().equals( - uidl.getStringVariable("filter"))) { - suggestionPopup.showSuggestions(currentSuggestions, currentPage, - totalMatches); - filtering = false; - if (!popupOpenerClicked && lastIndex != -1) { - // we're paging w/ arrows - if (lastIndex == 0) { - // going up, select last item - int lastItem = PAGELENTH - 1; - List items = suggestionPopup.menu.getItems(); - /* - * The first page can contain less than 10 items if the null - * selection item is filtered away - */ - if (lastItem >= items.size()) { - lastItem = items.size() - 1; - } - suggestionPopup.menu.selectItem((MenuItem) items - .get(lastItem)); - } else { - // going down, select first item - suggestionPopup.menu - .selectItem((MenuItem) suggestionPopup.menu - .getItems().get(0)); - } - lastIndex = -1; // reset - } - } - - // Calculate minumum textarea width - suggestionPopupMinWidth = minWidth(captions); - - popupOpenerClicked = false; - - updateRootWidth(); - } - - private void setPromptingOn() { - prompting = true; - addStyleDependentName(CLASSNAME_PROMPT); - tb.setText(inputPrompt); - } - - private void setPromptingOff(String text) { - tb.setText(text); - prompting = false; - removeStyleDependentName(CLASSNAME_PROMPT); - } - - public void onSuggestionSelected(FilterSelectSuggestion suggestion) { - currentSuggestion = suggestion; - String newKey; - if (suggestion.key.equals("")) { - // "nullselection" - newKey = ""; - } else { - // normal selection - newKey = String.valueOf(suggestion.getOptionKey()); - } - - String text = suggestion.getReplacementString(); - if ("".equals(newKey) && !focused) { - setPromptingOn(); - } else { - setPromptingOff(text); - } - setSelectedItemIcon(suggestion.getIconUri()); - if (!newKey.equals(selectedOptionKey)) { - selectedOptionKey = newKey; - client.updateVariable(paintableId, "selected", - new String[] { selectedOptionKey }, immediate); - // currentPage = -1; // forget the page - } - suggestionPopup.hide(); - } - - private void setSelectedItemIcon(String iconUri) { - if (iconUri == null) { - selectedItemIcon.setVisible(false); - updateRootWidth(); - } else { - selectedItemIcon.setUrl(iconUri); - selectedItemIcon.setVisible(true); - updateRootWidth(); - updateSelectedIconPosition(); - } - } - - private void updateSelectedIconPosition() { - // Position icon vertically to middle - int availableHeight = getOffsetHeight(); - int iconHeight = Util.getRequiredHeight(selectedItemIcon); - int marginTop = (availableHeight - iconHeight) / 2; - DOM.setStyleAttribute(selectedItemIcon.getElement(), "marginTop", - marginTop + "px"); - } - - public void onKeyDown(Widget sender, char keyCode, int modifiers) { - if (enabled && suggestionPopup.isAttached()) { - switch (keyCode) { - case KeyboardListener.KEY_DOWN: - suggestionPopup.selectNextItem(); - DOM.eventPreventDefault(DOM.eventGetCurrentEvent()); - break; - case KeyboardListener.KEY_UP: - suggestionPopup.selectPrevItem(); - DOM.eventPreventDefault(DOM.eventGetCurrentEvent()); - break; - case KeyboardListener.KEY_PAGEDOWN: - if (hasNextPage()) { - filterOptions(currentPage + 1, lastFilter); - } - break; - case KeyboardListener.KEY_PAGEUP: - if (currentPage > 0) { - filterOptions(currentPage - 1, lastFilter); - } - break; - case KeyboardListener.KEY_ENTER: - case KeyboardListener.KEY_TAB: - suggestionPopup.menu.doSelectedItemAction(); - break; - } - } - } - - public void onKeyPress(Widget sender, char keyCode, int modifiers) { - - } - - public void onKeyUp(Widget sender, char keyCode, int modifiers) { - if (enabled) { - switch (keyCode) { - case KeyboardListener.KEY_ENTER: - case KeyboardListener.KEY_TAB: - case KeyboardListener.KEY_SHIFT: - case KeyboardListener.KEY_CTRL: - case KeyboardListener.KEY_ALT: - ; // NOP - break; - case KeyboardListener.KEY_DOWN: - case KeyboardListener.KEY_UP: - case KeyboardListener.KEY_PAGEDOWN: - case KeyboardListener.KEY_PAGEUP: - if (suggestionPopup.isAttached()) { - break; - } else { - // open popup as from gadget - filterOptions(-1, ""); - lastFilter = ""; - tb.selectAll(); - break; - } - case KeyboardListener.KEY_ESCAPE: - if (currentSuggestion != null) { - String text = currentSuggestion.getReplacementString(); - setPromptingOff(text); - selectedOptionKey = currentSuggestion.key; - } else { - setPromptingOn(); - selectedOptionKey = null; - } - lastFilter = ""; - suggestionPopup.hide(); - break; - default: - filterOptions(currentPage); - break; - } - } - } - - /** - * Listener for popupopener - */ - public void onClick(Widget sender) { - if (enabled) { - // ask suggestionPopup if it was just closed, we are using GWT - // Popup's auto close feature - if (!suggestionPopup.isJustClosed()) { - filterOptions(-1, ""); - popupOpenerClicked = true; - lastFilter = ""; - } else if (selectedOptionKey == null) { - tb.setText(inputPrompt); - prompting = true; - } - DOM.eventPreventDefault(DOM.eventGetCurrentEvent()); - tb.setFocus(true); - tb.selectAll(); - - } - } - - /* - * Calculate minumum width for FilterSelect textarea - */ - private native int minWidth(String captions) - /*-{ - if(!captions || captions.length <= 0) - return 0; - captions = captions.split("|"); - var d = $wnd.document.createElement("div"); - var html = ""; - for(var i=0; i < captions.length; i++) { - html += "

" + captions[i] + "
"; - // TODO apply same CSS classname as in suggestionmenu - } - d.style.position = "absolute"; - d.style.top = "0"; - d.style.left = "0"; - d.style.visibility = "hidden"; - d.innerHTML = html; - $wnd.document.body.appendChild(d); - var w = d.offsetWidth; - $wnd.document.body.removeChild(d); - return w; - }-*/; - - public void onFocus(Widget sender) { - focused = true; - if (prompting) { - setPromptingOff(""); - } - addStyleDependentName("focus"); - } - - public void onLostFocus(Widget sender) { - focused = false; - if (!suggestionPopup.isAttached() || suggestionPopup.isJustClosed()) { - // typing so fast the popup was never opened, or it's just closed - suggestionPopup.menu.doSelectedItemAction(); - } - if (selectedOptionKey == null) { - setPromptingOn(); - } - removeStyleDependentName("focus"); - } - - public void focus() { - focused = true; - if (prompting) { - setPromptingOff(""); - } - tb.setFocus(true); - } - - @Override - public void setWidth(String width) { - if (width == null || width.equals("")) { - this.width = null; - } else { - this.width = width; - } - Util.setWidthExcludingPaddingAndBorder(this, width, 4); - updateRootWidth(); - } - - @Override - public void setHeight(String height) { - super.setHeight(height); - Util.setHeightExcludingPaddingAndBorder(tb, height, 3); - } - - private void updateRootWidth() { - if (width == null) { - /* - * When the width is not specified we must specify width for root - * div so the popupopener won't wrap to the next line and also so - * the size of the combobox won't change over time. - */ - int tbWidth = Util.getRequiredWidth(tb); - int openerWidth = Util.getRequiredWidth(popupOpener); - int iconWidth = Util.getRequiredWidth(selectedItemIcon); - - int w = tbWidth + openerWidth + iconWidth; - if (suggestionPopupMinWidth > w) { - setTextboxWidth(suggestionPopupMinWidth); - w = suggestionPopupMinWidth; - } else { - /* - * Firefox3 has its own way of doing rendering so we need to - * specify the width for the TextField to make sure it actually - * is rendered as wide as FF3 says it is - */ - tb.setWidth((tbWidth - getTextboxPadding()) + "px"); - } - super.setWidth((w) + "px"); - // Freeze the initial width, so that it won't change even if the - // icon size changes - width = w + "px"; - - } else { - /* - * When the width is specified we also want to explicitly specify - * widths for textbox and popupopener - */ - setTextboxWidth(getMainWidth() - getComponentPadding()); - - } - } - - private int getMainWidth() { - int componentWidth; - if (BrowserInfo.get().isIE6()) { - // Required in IE when textfield is wider than this.width - DOM.setStyleAttribute(getElement(), "overflow", "hidden"); - componentWidth = getOffsetWidth(); - DOM.setStyleAttribute(getElement(), "overflow", ""); - } else { - componentWidth = getOffsetWidth(); - } - return componentWidth; - } - - private void setTextboxWidth(int componentWidth) { - int padding = getTextboxPadding(); - int popupOpenerWidth = Util.getRequiredWidth(popupOpener); - int iconWidth = Util.getRequiredWidth(selectedItemIcon); - int textboxWidth = componentWidth - padding - popupOpenerWidth - - iconWidth; - if (textboxWidth < 0) { - textboxWidth = 0; - } - tb.setWidth(textboxWidth + "px"); - } - - private int getTextboxPadding() { - if (textboxPadding < 0) { - textboxPadding = Util.measureHorizontalPaddingAndBorder(tb - .getElement(), 4); - } - return textboxPadding; - } - - private int getComponentPadding() { - if (componentPadding < 0) { - componentPadding = Util.measureHorizontalPaddingAndBorder( - getElement(), 3); - } - return componentPadding; - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IForm.java b/src/com/vaadin/terminal/gwt/client/ui/IForm.java deleted file mode 100644 index 9e6c0f5ae2..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IForm.java +++ /dev/null @@ -1,288 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.Set; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.ui.ComplexPanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.Container; -import com.vaadin.terminal.gwt.client.IErrorMessage; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.RenderInformation; -import com.vaadin.terminal.gwt.client.RenderSpace; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; - -public class IForm extends ComplexPanel implements Container { - - private String height = ""; - - private String width = ""; - - public static final String CLASSNAME = "i-form"; - - private Container lo; - private Element legend = DOM.createLegend(); - private Element caption = DOM.createSpan(); - private Element errorIndicatorElement = DOM.createDiv(); - private Element desc = DOM.createDiv(); - private Icon icon; - private IErrorMessage errorMessage = new IErrorMessage(); - - private Element fieldContainer = DOM.createDiv(); - - private Element footerContainer = DOM.createDiv(); - - private Element fieldSet = DOM.createFieldSet(); - - private Container footer; - - private ApplicationConnection client; - - private RenderInformation renderInformation = new RenderInformation(); - - private int borderPaddingHorizontal; - - private int borderPaddingVertical; - - private boolean rendering = false; - - public IForm() { - setElement(DOM.createDiv()); - DOM.appendChild(getElement(), fieldSet); - setStyleName(CLASSNAME); - DOM.appendChild(fieldSet, legend); - DOM.appendChild(legend, caption); - DOM.setElementProperty(errorIndicatorElement, "className", - "i-errorindicator"); - DOM.setStyleAttribute(errorIndicatorElement, "display", "none"); - DOM.setInnerText(errorIndicatorElement, " "); // needed for IE - DOM.setElementProperty(desc, "className", "i-form-description"); - DOM.appendChild(fieldSet, desc); - DOM.appendChild(fieldSet, fieldContainer); - errorMessage.setVisible(false); - errorMessage.setStyleName(CLASSNAME + "-errormessage"); - DOM.appendChild(fieldSet, errorMessage.getElement()); - DOM.appendChild(fieldSet, footerContainer); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - rendering = true; - boolean measure = false; - if (this.client == null) { - this.client = client; - measure = true; - } - - if (client.updateComponent(this, uidl, false)) { - rendering = false; - return; - } - - if (measure) { - // Measure the border when the style names have been set - borderPaddingVertical = getOffsetHeight(); - int ow = getOffsetWidth(); - int dow = desc.getOffsetWidth(); - borderPaddingHorizontal = ow - dow; - } - - boolean legendEmpty = true; - if (uidl.hasAttribute("caption")) { - DOM.setInnerText(caption, uidl.getStringAttribute("caption")); - legendEmpty = false; - } else { - DOM.setInnerText(caption, ""); - } - if (uidl.hasAttribute("icon")) { - if (icon == null) { - icon = new Icon(client); - DOM.insertChild(legend, icon.getElement(), 0); - } - icon.setUri(uidl.getStringAttribute("icon")); - legendEmpty = false; - } else { - if (icon != null) { - DOM.removeChild(legend, icon.getElement()); - } - } - if (legendEmpty) { - DOM.setStyleAttribute(legend, "display", "none"); - } else { - DOM.setStyleAttribute(legend, "display", ""); - } - - if (uidl.hasAttribute("error")) { - final UIDL errorUidl = uidl.getErrors(); - errorMessage.updateFromUIDL(errorUidl); - errorMessage.setVisible(true); - - } else { - errorMessage.setVisible(false); - } - - if (uidl.hasAttribute("description")) { - DOM.setInnerHTML(desc, uidl.getStringAttribute("description")); - } else { - DOM.setInnerHTML(desc, ""); - } - - updateSize(); - // TODO Check if this is needed - client.runDescendentsLayout(this); - - final UIDL layoutUidl = uidl.getChildUIDL(0); - Container newLo = (Container) client.getPaintable(layoutUidl); - if (lo == null) { - lo = newLo; - add((Widget) lo, fieldContainer); - } else if (lo != newLo) { - client.unregisterPaintable(lo); - remove((Widget) lo); - lo = newLo; - add((Widget) lo, fieldContainer); - } - lo.updateFromUIDL(layoutUidl, client); - - if (uidl.getChildCount() > 1) { - // render footer - Container newFooter = (Container) client.getPaintable(uidl - .getChildUIDL(1)); - if (footer == null) { - add((Widget) newFooter, footerContainer); - footer = newFooter; - } else if (newFooter != footer) { - remove((Widget) footer); - client.unregisterPaintable(footer); - add((Widget) newFooter, footerContainer); - } - footer = newFooter; - footer.updateFromUIDL(uidl.getChildUIDL(1), client); - } else { - if (footer != null) { - remove((Widget) footer); - client.unregisterPaintable(footer); - } - } - - rendering = false; - } - - public void updateSize() { - - renderInformation.updateSize(getElement()); - - renderInformation.setContentAreaHeight(renderInformation - .getRenderedSize().getHeight() - - borderPaddingVertical); - if (BrowserInfo.get().isIE6()) { - getElement().getStyle().setProperty("overflow", "hidden"); - } - renderInformation.setContentAreaWidth(renderInformation - .getRenderedSize().getWidth() - - borderPaddingHorizontal); - } - - public RenderSpace getAllocatedSpace(Widget child) { - if (child == lo) { - int hPixels = 0; - if (!"".equals(height)) { - hPixels = getOffsetHeight(); - hPixels -= borderPaddingVertical; - hPixels -= footerContainer.getOffsetHeight(); - hPixels -= errorMessage.getOffsetHeight(); - hPixels -= desc.getOffsetHeight(); - - } - - return new RenderSpace(renderInformation.getContentAreaSize() - .getWidth(), hPixels); - } else if (child == footer) { - return new RenderSpace(footerContainer.getOffsetWidth(), 0); - } else { - ApplicationConnection.getConsole().error( - "Invalid child requested RenderSpace information"); - return null; - } - } - - public boolean hasChildComponent(Widget component) { - return component != null && (component == lo || component == footer); - } - - public void replaceChildComponent(Widget oldComponent, Widget newComponent) { - if (!hasChildComponent(oldComponent)) { - throw new IllegalArgumentException( - "Old component is not inside this Container"); - } - remove(oldComponent); - if (oldComponent == lo) { - lo = (Container) newComponent; - add((Widget) lo, fieldContainer); - } else { - footer = (Container) newComponent; - add((Widget) footer, footerContainer); - } - - } - - public boolean requestLayout(Set child) { - - if (height != null && width != null) { - /* - * If the height and width has been specified the child components - * cannot make the size of the layout change - */ - - return true; - } - - if (renderInformation.updateSize(getElement())) { - return false; - } else { - return true; - } - - } - - public void updateCaption(Paintable component, UIDL uidl) { - // NOP form don't render caption for neither field layout nor footer - // layout - } - - @Override - public void setHeight(String height) { - if (this.height.equals(height)) { - return; - } - - this.height = height; - super.setHeight(height); - - updateSize(); - } - - @Override - public void setWidth(String width) { - if (Util.equals(this.width, width)) { - return; - } - - this.width = width; - super.setWidth(width); - - updateSize(); - - if (!rendering && height.equals("")) { - // Width might affect height - Util.updateRelativeChildrenAndSendSizeUpdateEvent(client, this); - } - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IFormLayout.java b/src/com/vaadin/terminal/gwt/client/ui/IFormLayout.java deleted file mode 100644 index 6d4e62b7a8..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IFormLayout.java +++ /dev/null @@ -1,464 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Set; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.FlexTable; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.SimplePanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.Container; -import com.vaadin.terminal.gwt.client.ITooltip; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.RenderSpace; -import com.vaadin.terminal.gwt.client.StyleConstants; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; - -/** - * Two col Layout that places caption on left col and field on right col - */ -public class IFormLayout extends SimplePanel implements Container { - - private final static String CLASSNAME = "i-formlayout"; - - private ApplicationConnection client; - private IFormLayoutTable table; - - private String width = ""; - private String height = ""; - - private boolean rendering = false; - - public IFormLayout() { - super(); - setStylePrimaryName(CLASSNAME); - table = new IFormLayoutTable(); - setWidget(table); - } - - public class IFormLayoutTable extends FlexTable { - - private static final int COLUMN_CAPTION = 0; - private static final int COLUMN_ERRORFLAG = 1; - private static final int COLUMN_WIDGET = 2; - - private HashMap componentToCaption = new HashMap(); - private HashMap componentToError = new HashMap(); - - public IFormLayoutTable() { - DOM.setElementProperty(getElement(), "cellPadding", "0"); - DOM.setElementProperty(getElement(), "cellSpacing", "0"); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - final IMarginInfo margins = new IMarginInfo(uidl - .getIntAttribute("margins")); - - Element margin = getElement(); - setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_TOP, - margins.hasTop()); - setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_RIGHT, - margins.hasRight()); - setStyleName(margin, - CLASSNAME + "-" + StyleConstants.MARGIN_BOTTOM, margins - .hasBottom()); - setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_LEFT, - margins.hasLeft()); - - setStyleName(margin, CLASSNAME + "-" + "spacing", uidl - .hasAttribute("spacing")); - - int i = 0; - for (final Iterator it = uidl.getChildIterator(); it.hasNext(); i++) { - prepareCell(i, 1); - final UIDL childUidl = (UIDL) it.next(); - final Paintable p = client.getPaintable(childUidl); - Caption caption = componentToCaption.get(p); - if (caption == null) { - caption = new Caption(p, client); - componentToCaption.put(p, caption); - } - ErrorFlag error = componentToError.get(p); - if (error == null) { - error = new ErrorFlag(); - componentToError.put(p, error); - } - prepareCell(i, COLUMN_WIDGET); - final Paintable oldComponent = (Paintable) getWidget(i, - COLUMN_WIDGET); - if (oldComponent == null) { - setWidget(i, COLUMN_WIDGET, (Widget) p); - } else if (oldComponent != p) { - client.unregisterPaintable(oldComponent); - setWidget(i, COLUMN_WIDGET, (Widget) p); - } - getCellFormatter().setStyleName(i, COLUMN_WIDGET, - CLASSNAME + "-contentcell"); - getCellFormatter().setStyleName(i, COLUMN_CAPTION, - CLASSNAME + "-captioncell"); - setWidget(i, COLUMN_CAPTION, caption); - - setContentWidth(i); - - getCellFormatter().setStyleName(i, COLUMN_ERRORFLAG, - CLASSNAME + "-errorcell"); - setWidget(i, COLUMN_ERRORFLAG, error); - - p.updateFromUIDL(childUidl, client); - - String rowstyles = CLASSNAME + "-row"; - if (i == 0) { - rowstyles += " " + CLASSNAME + "-firstrow"; - } - if (!it.hasNext()) { - rowstyles += " " + CLASSNAME + "-lastrow"; - } - - getRowFormatter().setStyleName(i, rowstyles); - - } - - while (getRowCount() > i) { - final Paintable p = (Paintable) getWidget(i, COLUMN_WIDGET); - client.unregisterPaintable(p); - componentToCaption.remove(p); - removeRow(i); - } - - /* - * Must update relative sized fields last when it is clear how much - * space they are allowed to use - */ - for (Paintable p : componentToCaption.keySet()) { - client.handleComponentRelativeSize((Widget) p); - } - } - - public void setContentWidths() { - for (int row = 0; row < getRowCount(); row++) { - setContentWidth(row); - } - } - - private void setContentWidth(int row) { - String width = ""; - if (!isDynamicWidth()) { - width = "100%"; - } - getCellFormatter().setWidth(row, COLUMN_WIDGET, width); - } - - public void replaceChildComponent(Widget oldComponent, - Widget newComponent) { - int i; - for (i = 0; i < getRowCount(); i++) { - Widget candidate = getWidget(i, COLUMN_WIDGET); - if (oldComponent == candidate) { - final Caption newCap = new Caption( - (Paintable) newComponent, client); - componentToCaption.put((Paintable) newComponent, newCap); - ErrorFlag error = componentToError.get(newComponent); - if (error == null) { - error = new ErrorFlag(); - componentToError.put((Paintable) newComponent, error); - } - - setWidget(i, COLUMN_CAPTION, newCap); - setWidget(i, COLUMN_ERRORFLAG, error); - setWidget(i, COLUMN_WIDGET, newComponent); - break; - } - } - - } - - public boolean hasChildComponent(Widget component) { - return componentToCaption.containsKey(component); - } - - public void updateCaption(Paintable component, UIDL uidl) { - final Caption c = componentToCaption.get(component); - if (c != null) { - c.updateCaption(uidl); - } - final ErrorFlag e = componentToError.get(component); - if (e != null) { - e.updateFromUIDL(uidl, component); - } - - } - - public int getAllocatedWidth(Widget child, int availableWidth) { - Caption caption = componentToCaption.get(child); - ErrorFlag error = componentToError.get(child); - int width = availableWidth; - - if (caption != null) { - width -= DOM.getParent(caption.getElement()).getOffsetWidth(); - } - if (error != null) { - width -= DOM.getParent(error.getElement()).getOffsetWidth(); - } - - return width; - } - - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - rendering = true; - - this.client = client; - - if (client.updateComponent(this, uidl, true)) { - rendering = false; - return; - } - - table.updateFromUIDL(uidl, client); - - rendering = false; - } - - public boolean isDynamicWidth() { - return width.equals(""); - } - - public boolean hasChildComponent(Widget component) { - return table.hasChildComponent(component); - } - - public void replaceChildComponent(Widget oldComponent, Widget newComponent) { - table.replaceChildComponent(oldComponent, newComponent); - } - - public void updateCaption(Paintable component, UIDL uidl) { - table.updateCaption(component, uidl); - } - - public class Caption extends HTML { - - public static final String CLASSNAME = "i-caption"; - - private final Paintable owner; - - private Element requiredFieldIndicator; - - private Icon icon; - - private Element captionText; - - private final ApplicationConnection client; - - /** - * - * @param component - * optional owner of caption. If not set, getOwner will - * return null - * @param client - */ - public Caption(Paintable component, ApplicationConnection client) { - super(); - this.client = client; - owner = component; - setStyleName(CLASSNAME); - sinkEvents(ITooltip.TOOLTIP_EVENTS); - } - - public void updateCaption(UIDL uidl) { - setVisible(!uidl.getBooleanAttribute("invisible")); - - setStyleName(getElement(), "i-disabled", uidl - .hasAttribute("disabled")); - - boolean isEmpty = true; - - if (uidl.hasAttribute("icon")) { - if (icon == null) { - icon = new Icon(client); - - DOM.insertChild(getElement(), icon.getElement(), 0); - } - icon.setUri(uidl.getStringAttribute("icon")); - isEmpty = false; - } else { - if (icon != null) { - DOM.removeChild(getElement(), icon.getElement()); - icon = null; - } - - } - - if (uidl.hasAttribute("caption")) { - if (captionText == null) { - captionText = DOM.createSpan(); - DOM.insertChild(getElement(), captionText, icon == null ? 0 - : 1); - } - String c = uidl.getStringAttribute("caption"); - if (c == null) { - c = ""; - } else { - isEmpty = false; - } - DOM.setInnerText(captionText, c); - } else { - // TODO should span also be removed - } - - if (uidl.hasAttribute("description")) { - if (captionText != null) { - addStyleDependentName("hasdescription"); - } else { - removeStyleDependentName("hasdescription"); - } - } - - if (uidl.getBooleanAttribute("required")) { - if (requiredFieldIndicator == null) { - requiredFieldIndicator = DOM.createSpan(); - DOM.setInnerText(requiredFieldIndicator, "*"); - DOM.setElementProperty(requiredFieldIndicator, "className", - "i-required-field-indicator"); - DOM.appendChild(getElement(), requiredFieldIndicator); - } - } else { - if (requiredFieldIndicator != null) { - DOM.removeChild(getElement(), requiredFieldIndicator); - requiredFieldIndicator = null; - } - } - - // Workaround for IE weirdness, sometimes returns bad height in some - // circumstances when Caption is empty. See #1444 - // IE7 bugs more often. I wonder what happens when IE8 arrives... - if (Util.isIE()) { - if (isEmpty) { - setHeight("0px"); - DOM.setStyleAttribute(getElement(), "overflow", "hidden"); - } else { - setHeight(""); - DOM.setStyleAttribute(getElement(), "overflow", ""); - } - - } - - } - - /** - * Returns Paintable for which this Caption belongs to. - * - * @return owner Widget - */ - public Paintable getOwner() { - return owner; - } - - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - if (client != null) { - client.handleTooltipEvent(event, owner); - } - } - } - - private class ErrorFlag extends HTML { - private static final String CLASSNAME = IFormLayout.CLASSNAME - + "-error-indicator"; - Element errorIndicatorElement; - private Paintable owner; - - public ErrorFlag() { - setStyleName(CLASSNAME); - sinkEvents(ITooltip.TOOLTIP_EVENTS); - } - - public void updateFromUIDL(UIDL uidl, Paintable component) { - owner = component; - if (uidl.hasAttribute("error") - && !uidl.getBooleanAttribute("hideErrors")) { - if (errorIndicatorElement == null) { - errorIndicatorElement = DOM.createDiv(); - DOM.setInnerHTML(errorIndicatorElement, " "); - DOM.setElementProperty(errorIndicatorElement, "className", - "i-errorindicator"); - DOM.appendChild(getElement(), errorIndicatorElement); - } - - } else if (errorIndicatorElement != null) { - DOM.removeChild(getElement(), errorIndicatorElement); - errorIndicatorElement = null; - } - } - - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - if (owner != null) { - client.handleTooltipEvent(event, owner); - } - } - - } - - public boolean requestLayout(Set child) { - if (height.equals("") || width.equals("")) { - // A dynamic size might change due to children changes - return false; - } - - return true; - } - - public RenderSpace getAllocatedSpace(Widget child) { - int width = 0; - int height = 0; - - if (!this.width.equals("")) { - int availableWidth = getOffsetWidth(); - width = table.getAllocatedWidth(child, availableWidth); - } - - return new RenderSpace(width, height, false); - } - - @Override - public void setHeight(String height) { - if (this.height.equals(height)) { - return; - } - - this.height = height; - super.setHeight(height); - } - - @Override - public void setWidth(String width) { - if (this.width.equals(width)) { - return; - } - - this.width = width; - super.setWidth(width); - - if (!rendering) { - table.setContentWidths(); - if (height.equals("")) { - // Width might affect height - Util.updateRelativeChildrenAndSendSizeUpdateEvent(client, this); - } - } - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IGridLayout.java b/src/com/vaadin/terminal/gwt/client/ui/IGridLayout.java deleted file mode 100644 index 07bb7584b4..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IGridLayout.java +++ /dev/null @@ -1,1018 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import com.google.gwt.dom.client.DivElement; -import com.google.gwt.dom.client.Document; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.ui.AbsolutePanel; -import com.google.gwt.user.client.ui.SimplePanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.Container; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.RenderSpace; -import com.vaadin.terminal.gwt.client.StyleConstants; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; -import com.vaadin.terminal.gwt.client.ui.layout.CellBasedLayout; -import com.vaadin.terminal.gwt.client.ui.layout.ChildComponentContainer; - -public class IGridLayout extends SimplePanel implements Paintable, Container { - - public static final String CLASSNAME = "i-gridlayout"; - - private DivElement margin = Document.get().createDivElement(); - - private final AbsolutePanel canvas = new AbsolutePanel(); - - private ApplicationConnection client; - - protected HashMap widgetToComponentContainer = new HashMap(); - - private HashMap paintableToCell = new HashMap(); - - private int spacingPixelsHorizontal; - private int spacingPixelsVertical; - - private int[] columnWidths; - private int[] rowHeights; - - private String height; - - private String width; - - private int[] colExpandRatioArray; - - private int[] rowExpandRatioArray; - - private int[] minColumnWidths; - - private int[] minRowHeights; - - private boolean rendering; - - private HashMap nonRenderedWidgets; - - private boolean sizeChangedDuringRendering = false; - - public IGridLayout() { - super(); - getElement().appendChild(margin); - setStyleName(CLASSNAME); - setWidget(canvas); - } - - @Override - protected Element getContainerElement() { - return margin.cast(); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - rendering = true; - this.client = client; - - if (client.updateComponent(this, uidl, true)) { - rendering = false; - return; - } - - boolean mightToggleVScrollBar = "".equals(height) && !"".equals(width); - boolean mightToggleHScrollBar = "".equals(width) && !"".equals(height); - int wBeforeRender = 0; - int hBeforeRender = 0; - if (mightToggleHScrollBar || mightToggleVScrollBar) { - wBeforeRender = canvas.getOffsetWidth(); - hBeforeRender = getOffsetHeight(); - } - canvas.setWidth("0px"); - - handleMargins(uidl); - detectSpacing(uidl); - - int cols = uidl.getIntAttribute("w"); - int rows = uidl.getIntAttribute("h"); - - columnWidths = new int[cols]; - rowHeights = new int[rows]; - - if (cells == null) { - cells = new Cell[cols][rows]; - } else if (cells.length != cols || cells[0].length != rows) { - Cell[][] newCells = new Cell[cols][rows]; - for (int i = 0; i < cells.length; i++) { - for (int j = 0; j < cells[i].length; j++) { - if (i < cols && j < rows) { - newCells[i][j] = cells[i][j]; - } - } - } - cells = newCells; - } - - nonRenderedWidgets = (HashMap) widgetToComponentContainer - .clone(); - - final int[] alignments = uidl.getIntArrayAttribute("alignments"); - int alignmentIndex = 0; - - LinkedList pendingCells = new LinkedList(); - - LinkedList relativeHeighted = new LinkedList(); - - for (final Iterator i = uidl.getChildIterator(); i.hasNext();) { - final UIDL r = (UIDL) i.next(); - if ("gr".equals(r.getTag())) { - for (final Iterator j = r.getChildIterator(); j.hasNext();) { - final UIDL c = (UIDL) j.next(); - if ("gc".equals(c.getTag())) { - Cell cell = getCell(c); - if (cell.hasContent()) { - boolean rendered = cell.renderIfNoRelativeWidth(); - cell.alignment = alignments[alignmentIndex++]; - if (!rendered) { - pendingCells.add(cell); - } - - if (cell.colspan > 1) { - storeColSpannedCell(cell); - } else if (rendered) { - // strore non-colspanned widths to columnWidth - // array - if (columnWidths[cell.col] < cell.getWidth()) { - columnWidths[cell.col] = cell.getWidth(); - } - } - if (cell.hasRelativeHeight()) { - relativeHeighted.add(cell); - } - } - } - } - } - } - - distributeColSpanWidths(); - colExpandRatioArray = uidl.getIntArrayAttribute("colExpand"); - rowExpandRatioArray = uidl.getIntArrayAttribute("rowExpand"); - - minColumnWidths = cloneArray(columnWidths); - expandColumns(); - - renderRemainingComponentsWithNoRelativeHeight(pendingCells); - - detectRowHeights(); - - expandRows(); - - renderRemainingComponents(pendingCells); - - for (Cell cell : relativeHeighted) { - Widget widget2 = cell.cc.getWidget(); - client.handleComponentRelativeSize(widget2); - cell.cc.updateWidgetSize(); - } - - layoutCells(); - - // clean non rendered components - for (Widget w : nonRenderedWidgets.keySet()) { - ChildComponentContainer childComponentContainer = widgetToComponentContainer - .get(w); - paintableToCell.remove(w); - widgetToComponentContainer.remove(w); - childComponentContainer.removeFromParent(); - client.unregisterPaintable((Paintable) w); - } - nonRenderedWidgets = null; - - rendering = false; - sizeChangedDuringRendering = false; - - boolean needsRelativeSizeCheck = false; - - if (mightToggleHScrollBar && wBeforeRender != canvas.getOffsetWidth()) { - needsRelativeSizeCheck = true; - } - if (mightToggleVScrollBar && hBeforeRender != getOffsetHeight()) { - needsRelativeSizeCheck = true; - } - if (needsRelativeSizeCheck) { - client.handleComponentRelativeSize(this); - } - } - - private static int[] cloneArray(int[] toBeCloned) { - int[] clone = new int[toBeCloned.length]; - for (int i = 0; i < clone.length; i++) { - clone[i] = toBeCloned[i] * 1; - } - return clone; - } - - private void expandRows() { - if (!"".equals(height)) { - int usedSpace = minRowHeights[0]; - for (int i = 1; i < minRowHeights.length; i++) { - usedSpace += spacingPixelsVertical + minRowHeights[i]; - } - int availableSpace = getOffsetHeight() - marginTopAndBottom; - int excessSpace = availableSpace - usedSpace; - int distributed = 0; - if (excessSpace > 0) { - for (int i = 0; i < rowHeights.length; i++) { - int ew = excessSpace * rowExpandRatioArray[i] / 1000; - rowHeights[i] = minRowHeights[i] + ew; - distributed += ew; - } - excessSpace -= distributed; - int c = 0; - while (excessSpace > 0) { - rowHeights[c % rowHeights.length]++; - excessSpace--; - c++; - } - } - } - } - - @Override - public void setHeight(String height) { - super.setHeight(height); - if (!height.equals(this.height)) { - this.height = height; - if (rendering) { - sizeChangedDuringRendering = true; - } else { - expandRows(); - layoutCells(); - for (Paintable c : paintableToCell.keySet()) { - client.handleComponentRelativeSize((Widget) c); - } - } - } - } - - @Override - public void setWidth(String width) { - super.setWidth(width); - if (!width.equals(this.width)) { - this.width = width; - if (rendering) { - sizeChangedDuringRendering = true; - } else { - int[] oldWidths = cloneArray(columnWidths); - expandColumns(); - boolean heightChanged = false; - HashSet dirtyRows = null; - for (int i = 0; i < oldWidths.length; i++) { - if (columnWidths[i] != oldWidths[i]) { - Cell[] column = cells[i]; - for (int j = 0; j < column.length; j++) { - Cell c = column[j]; - if (c != null && c.cc != null - && c.widthCanAffectHeight()) { - c.cc.setContainerSize(c.getAvailableWidth(), c - .getAvailableHeight()); - client.handleComponentRelativeSize(c.cc - .getWidget()); - c.cc.updateWidgetSize(); - int newHeight = c.getHeight(); - if (columnWidths[i] < oldWidths[i] - && newHeight > minRowHeights[j]) { - minRowHeights[j] = newHeight; - if (newHeight > rowHeights[j]) { - rowHeights[j] = newHeight; - heightChanged = true; - } - } else if (newHeight < minRowHeights[j]) { - // need to recalculate new minimum height - // for this row - if (dirtyRows == null) { - dirtyRows = new HashSet(); - } - dirtyRows.add(j); - } - } - } - } - } - if (dirtyRows != null) { - /* flag indicating that there is a potential row shrinking */ - boolean rowMayShrink = false; - for (Integer rowIndex : dirtyRows) { - int oldMinimum = minRowHeights[rowIndex]; - int newMinimum = 0; - for (int colIndex = 0; colIndex < columnWidths.length; colIndex++) { - Cell cell = cells[colIndex][rowIndex]; - if (cell != null && !cell.hasRelativeHeight() - && cell.getHeight() > newMinimum) { - newMinimum = cell.getHeight(); - } - } - if (newMinimum < oldMinimum) { - minRowHeights[rowIndex] = rowHeights[rowIndex] = newMinimum; - rowMayShrink = true; - } - } - if (rowMayShrink) { - distributeRowSpanHeights(); - minRowHeights = cloneArray(rowHeights); - heightChanged = true; - } - - } - layoutCells(); - for (Paintable c : paintableToCell.keySet()) { - client.handleComponentRelativeSize((Widget) c); - } - if (heightChanged && "".equals(height)) { - Util.notifyParentOfSizeChange(this, false); - } - } - } - } - - private void expandColumns() { - if (!"".equals(width)) { - int usedSpace = minColumnWidths[0]; - for (int i = 1; i < minColumnWidths.length; i++) { - usedSpace += spacingPixelsHorizontal + minColumnWidths[i]; - } - canvas.setWidth(""); - int availableSpace = canvas.getOffsetWidth(); - int excessSpace = availableSpace - usedSpace; - int distributed = 0; - if (excessSpace > 0) { - for (int i = 0; i < columnWidths.length; i++) { - int ew = excessSpace * colExpandRatioArray[i] / 1000; - columnWidths[i] = minColumnWidths[i] + ew; - distributed += ew; - } - excessSpace -= distributed; - int c = 0; - while (excessSpace > 0) { - columnWidths[c % columnWidths.length]++; - excessSpace--; - c++; - } - } - } - } - - private void layoutCells() { - int x = 0; - int y = 0; - for (int i = 0; i < cells.length; i++) { - y = 0; - for (int j = 0; j < cells[i].length; j++) { - Cell cell = cells[i][j]; - if (cell != null) { - cell.layout(x, y); - } - y += rowHeights[j] + spacingPixelsVertical; - } - x += columnWidths[i] + spacingPixelsHorizontal; - } - - if ("".equals(width)) { - canvas.setWidth((x - spacingPixelsHorizontal) + "px"); - } else { - // main element defines width - canvas.setWidth(""); - } - int canvasHeight; - if ("".equals(height)) { - canvasHeight = y - spacingPixelsVertical; - } else { - canvasHeight = getOffsetHeight() - marginTopAndBottom; - } - canvas.setHeight(canvasHeight + "px"); - } - - private void renderRemainingComponents(LinkedList pendingCells) { - for (Cell cell : pendingCells) { - cell.render(); - } - } - - private void detectRowHeights() { - - // collect min rowheight from non-rowspanned cells - for (int i = 0; i < cells.length; i++) { - for (int j = 0; j < cells[i].length; j++) { - Cell cell = cells[i][j]; - if (cell != null) { - /* - * Setting fixing container width may in some situations - * affect height. Example: Label with wrapping text without - * or with relative width. - */ - if (cell.cc != null && cell.widthCanAffectHeight()) { - cell.cc.setWidth(cell.getAvailableWidth() + "px"); - cell.cc.updateWidgetSize(); - } - if (cell.rowspan == 1) { - if (!cell.hasRelativeHeight() - && rowHeights[j] < cell.getHeight()) { - rowHeights[j] = cell.getHeight(); - } - } else { - storeRowSpannedCell(cell); - } - } - } - } - - distributeRowSpanHeights(); - - minRowHeights = cloneArray(rowHeights); - } - - private void storeRowSpannedCell(Cell cell) { - SpanList l = null; - for (SpanList list : rowSpans) { - if (list.span < cell.rowspan) { - continue; - } else { - // insert before this - l = list; - break; - } - } - if (l == null) { - l = new SpanList(cell.rowspan); - rowSpans.add(l); - } else if (l.span != cell.rowspan) { - SpanList newL = new SpanList(cell.rowspan); - rowSpans.add(rowSpans.indexOf(l), newL); - l = newL; - } - l.cells.add(cell); - } - - private void renderRemainingComponentsWithNoRelativeHeight( - LinkedList pendingCells) { - - for (Iterator iterator = pendingCells.iterator(); iterator.hasNext();) { - Cell cell = (Cell) iterator.next(); - if (!cell.hasRelativeHeight()) { - cell.render(); - iterator.remove(); - } - } - - } - - /** - * Iterates colspanned cells, ensures cols have enough space to accommodate - * them - */ - private void distributeColSpanWidths() { - for (SpanList list : colSpans) { - for (Cell cell : list.cells) { - int width = cell.getWidth(); - int allocated = columnWidths[cell.col]; - for (int i = 1; i < cell.colspan; i++) { - allocated += spacingPixelsHorizontal - + columnWidths[cell.col + i]; - } - if (allocated < width) { - // columnWidths needs to be expanded due colspanned cell - int neededExtraSpace = width - allocated; - int spaceForColunms = neededExtraSpace / cell.colspan; - for (int i = 0; i < cell.colspan; i++) { - int col = cell.col + i; - columnWidths[col] += spaceForColunms; - neededExtraSpace -= spaceForColunms; - } - if (neededExtraSpace > 0) { - for (int i = 0; i < cell.colspan; i++) { - int col = cell.col + i; - columnWidths[col] += 1; - neededExtraSpace -= 1; - if (neededExtraSpace == 0) { - break; - } - } - } - } - } - } - } - - /** - * Iterates rowspanned cells, ensures rows have enough space to accommodate - * them - */ - private void distributeRowSpanHeights() { - for (SpanList list : rowSpans) { - for (Cell cell : list.cells) { - int height = cell.getHeight(); - int allocated = rowHeights[cell.row]; - for (int i = 1; i < cell.rowspan; i++) { - allocated += spacingPixelsVertical - + rowHeights[cell.row + i]; - } - if (allocated < height) { - // columnWidths needs to be expanded due colspanned cell - int neededExtraSpace = height - allocated; - int spaceForColunms = neededExtraSpace / cell.rowspan; - for (int i = 0; i < cell.rowspan; i++) { - int row = cell.row + i; - rowHeights[row] += spaceForColunms; - neededExtraSpace -= spaceForColunms; - } - if (neededExtraSpace > 0) { - for (int i = 0; i < cell.rowspan; i++) { - int row = cell.row + i; - rowHeights[row] += 1; - neededExtraSpace -= 1; - if (neededExtraSpace == 0) { - break; - } - } - } - } - } - } - } - - private LinkedList colSpans = new LinkedList(); - private LinkedList rowSpans = new LinkedList(); - - private int marginTopAndBottom; - - private class SpanList { - final int span; - List cells = new LinkedList(); - - public SpanList(int span) { - this.span = span; - } - } - - private void storeColSpannedCell(Cell cell) { - SpanList l = null; - for (SpanList list : colSpans) { - if (list.span < cell.colspan) { - continue; - } else { - // insert before this - l = list; - break; - } - } - if (l == null) { - l = new SpanList(cell.colspan); - colSpans.add(l); - } else if (l.span != cell.colspan) { - - SpanList newL = new SpanList(cell.colspan); - colSpans.add(colSpans.indexOf(l), newL); - l = newL; - } - l.cells.add(cell); - } - - private void detectSpacing(UIDL uidl) { - DivElement spacingmeter = Document.get().createDivElement(); - spacingmeter.setClassName(CLASSNAME + "-" + "spacing-" - + (uidl.getBooleanAttribute("spacing") ? "on" : "off")); - spacingmeter.getStyle().setProperty("width", "0"); - spacingmeter.getStyle().setProperty("height", "0"); - canvas.getElement().appendChild(spacingmeter); - spacingPixelsHorizontal = spacingmeter.getOffsetWidth(); - spacingPixelsVertical = spacingmeter.getOffsetHeight(); - canvas.getElement().removeChild(spacingmeter); - } - - private void handleMargins(UIDL uidl) { - final IMarginInfo margins = new IMarginInfo(uidl - .getIntAttribute("margins")); - - String styles = CLASSNAME + "-margin"; - if (margins.hasTop()) { - styles += " " + CLASSNAME + "-" + StyleConstants.MARGIN_TOP; - } - if (margins.hasRight()) { - styles += " " + CLASSNAME + "-" + StyleConstants.MARGIN_RIGHT; - } - if (margins.hasBottom()) { - styles += " " + CLASSNAME + "-" + StyleConstants.MARGIN_BOTTOM; - } - if (margins.hasLeft()) { - styles += " " + CLASSNAME + "-" + StyleConstants.MARGIN_LEFT; - } - margin.setClassName(styles); - - marginTopAndBottom = margin.getOffsetHeight() - - canvas.getOffsetHeight(); - } - - public boolean hasChildComponent(Widget component) { - return paintableToCell.containsKey(component); - } - - public void replaceChildComponent(Widget oldComponent, Widget newComponent) { - ChildComponentContainer componentContainer = widgetToComponentContainer - .remove(oldComponent); - if (componentContainer == null) { - return; - } - - componentContainer.setWidget(newComponent); - widgetToComponentContainer.put(newComponent, componentContainer); - - paintableToCell.put((Paintable) newComponent, paintableToCell - .get(oldComponent)); - } - - public void updateCaption(Paintable component, UIDL uidl) { - ChildComponentContainer cc = widgetToComponentContainer.get(component); - if (cc != null) { - cc.updateCaption(uidl, client); - } - if (!rendering) { - // ensure rel size details are updated - paintableToCell.get(component).updateRelSizeStatus(uidl); - } - } - - public boolean requestLayout(final Set changedChildren) { - boolean needsLayout = false; - boolean reDistributeColSpanWidths = false; - boolean reDistributeRowSpanHeights = false; - int offsetHeight = canvas.getOffsetHeight(); - int offsetWidth = canvas.getOffsetWidth(); - if ("".equals(width) || "".equals(height)) { - needsLayout = true; - } - ArrayList dirtyColumns = new ArrayList(); - ArrayList dirtyRows = new ArrayList(); - for (Paintable paintable : changedChildren) { - - Cell cell = paintableToCell.get(paintable); - if (!cell.hasRelativeHeight() || !cell.hasRelativeWidth()) { - // cell sizes will only stay still if only relatively - // sized - // components - // check if changed child affects min col widths - cell.cc.setWidth(""); - cell.cc.setHeight(""); - - cell.cc.updateWidgetSize(); - int width = cell.getWidth(); - int allocated = columnWidths[cell.col]; - for (int i = 1; i < cell.colspan; i++) { - allocated += spacingPixelsHorizontal - + columnWidths[cell.col + i]; - } - if (allocated < width) { - needsLayout = true; - if (cell.colspan == 1) { - // do simple column width expansion - columnWidths[cell.col] = minColumnWidths[cell.col] = width; - } else { - // mark that col span expansion is needed - reDistributeColSpanWidths = true; - } - } else if (allocated != width) { - // size is smaller thant allocated, column might - // shrink - dirtyColumns.add(cell.col); - } - - int height = cell.getHeight(); - - allocated = rowHeights[cell.row]; - for (int i = 1; i < cell.rowspan; i++) { - allocated += spacingPixelsVertical - + rowHeights[cell.row + i]; - } - if (allocated < height) { - needsLayout = true; - if (cell.rowspan == 1) { - // do simple row expansion - rowHeights[cell.row] = minRowHeights[cell.row] = height; - } else { - // mark that row span expansion is needed - reDistributeRowSpanHeights = true; - } - } else if (allocated != height) { - // size is smaller than allocated, row might shrink - dirtyRows.add(cell.row); - } - } - } - - if (dirtyColumns.size() > 0) { - for (Integer colIndex : dirtyColumns) { - int colW = 0; - for (int i = 0; i < rowHeights.length; i++) { - Cell cell = cells[colIndex][i]; - if (cell != null && cell.getChildUIDL() != null - && !cell.hasRelativeWidth() && cell.colspan == 1) { - int width = cell.getWidth(); - if (width > colW) { - colW = width; - } - } - } - minColumnWidths[colIndex] = colW; - } - needsLayout = true; - // ensure colspanned columns have enough space - columnWidths = cloneArray(minColumnWidths); - distributeColSpanWidths(); - reDistributeColSpanWidths = false; - } - - if (reDistributeColSpanWidths) { - distributeColSpanWidths(); - } - - if (dirtyRows.size() > 0) { - needsLayout = true; - for (Integer rowIndex : dirtyRows) { - // recalculate min row height - int rowH = minRowHeights[rowIndex] = 0; - // loop all columns on row rowIndex - for (int i = 0; i < columnWidths.length; i++) { - Cell cell = cells[i][rowIndex]; - if (cell != null && cell.getChildUIDL() != null - && !cell.hasRelativeHeight() && cell.rowspan == 1) { - int h = cell.getHeight(); - if (h > rowH) { - rowH = h; - } - } - } - minRowHeights[rowIndex] = rowH; - } - // TODO could check only some row spans - rowHeights = cloneArray(minRowHeights); - distributeRowSpanHeights(); - reDistributeRowSpanHeights = false; - } - - if (reDistributeRowSpanHeights) { - distributeRowSpanHeights(); - } - - if (needsLayout) { - expandColumns(); - expandRows(); - layoutCells(); - // loop all relative sized components and update their size - for (int i = 0; i < cells.length; i++) { - for (int j = 0; j < cells[i].length; j++) { - Cell cell = cells[i][j]; - if (cell != null - && cell.cc != null - && (cell.hasRelativeHeight() || cell - .hasRelativeWidth())) { - client.handleComponentRelativeSize(cell.cc.getWidget()); - } - } - } - } - if (canvas.getOffsetHeight() != offsetHeight - || canvas.getOffsetWidth() != offsetWidth) { - return false; - } else { - return true; - } - } - - public RenderSpace getAllocatedSpace(Widget child) { - Cell cell = paintableToCell.get(child); - assert cell != null; - return cell.getAllocatedSpace(); - } - - private Cell[][] cells; - - /** - * Private helper class. - */ - private class Cell { - private boolean relHeight = false; - private boolean relWidth = false; - private boolean widthCanAffectHeight = false; - - public Cell(UIDL c) { - row = c.getIntAttribute("y"); - col = c.getIntAttribute("x"); - setUidl(c); - } - - public boolean widthCanAffectHeight() { - return widthCanAffectHeight; - } - - public boolean hasRelativeHeight() { - return relHeight; - } - - public RenderSpace getAllocatedSpace() { - return new RenderSpace(getAvailableWidth() - - cc.getCaptionWidthAfterComponent(), getAvailableHeight() - - cc.getCaptionHeightAboveComponent()); - } - - public boolean hasContent() { - return childUidl != null; - } - - /** - * @return total of spanned cols - */ - private int getAvailableWidth() { - int width = columnWidths[col]; - for (int i = 1; i < colspan; i++) { - width += spacingPixelsHorizontal + columnWidths[col + i]; - } - return width; - } - - /** - * @return total of spanned rows - */ - private int getAvailableHeight() { - int height = rowHeights[row]; - for (int i = 1; i < rowspan; i++) { - height += spacingPixelsVertical + rowHeights[row + i]; - } - return height; - } - - public void layout(int x, int y) { - if (cc != null && cc.isAttached()) { - canvas.setWidgetPosition(cc, x, y); - cc.setContainerSize(getAvailableWidth(), getAvailableHeight()); - cc.setAlignment(new AlignmentInfo(alignment)); - cc.updateAlignments(getAvailableWidth(), getAvailableHeight()); - } - } - - public int getWidth() { - if (cc != null) { - int w = cc.getWidgetSize().getWidth() - + cc.getCaptionWidthAfterComponent(); - return w; - } else { - return 0; - } - } - - public int getHeight() { - if (cc != null) { - return cc.getWidgetSize().getHeight() - + cc.getCaptionHeightAboveComponent(); - } else { - return 0; - } - } - - public boolean renderIfNoRelativeWidth() { - if (childUidl == null) { - return false; - } - if (!hasRelativeWidth()) { - render(); - return true; - } else { - return false; - } - } - - protected boolean hasRelativeWidth() { - return relWidth; - } - - protected void render() { - assert childUidl != null; - - Paintable paintable = client.getPaintable(childUidl); - assert paintable != null; - if (cc == null || cc.getWidget() != paintable) { - if (widgetToComponentContainer.containsKey(paintable)) { - cc = widgetToComponentContainer.get(paintable); - cc.setWidth(""); - cc.setHeight(""); - } else { - cc = new ChildComponentContainer((Widget) paintable, - CellBasedLayout.ORIENTATION_VERTICAL); - widgetToComponentContainer.put((Widget) paintable, cc); - paintableToCell.put(paintable, this); - cc.setWidth(""); - canvas.add(cc, 0, 0); - } - } - cc.renderChild(childUidl, client, -1); - if (sizeChangedDuringRendering && Util.isCached(childUidl)) { - client.handleComponentRelativeSize(cc.getWidget()); - } - cc.updateWidgetSize(); - nonRenderedWidgets.remove(paintable); - } - - public UIDL getChildUIDL() { - return childUidl; - } - - final int row; - final int col; - int colspan = 1; - int rowspan = 1; - UIDL childUidl; - int alignment; - ChildComponentContainer cc; - - public void setUidl(UIDL c) { - // Set cell width - colspan = c.hasAttribute("w") ? c.getIntAttribute("w") : 1; - // Set cell height - rowspan = c.hasAttribute("h") ? c.getIntAttribute("h") : 1; - // ensure we will lose reference to old cells, now overlapped by - // this cell - for (int i = 0; i < colspan; i++) { - for (int j = 0; j < rowspan; j++) { - if (i > 0 || j > 0) { - cells[col + i][row + j] = null; - } - } - } - - c = c.getChildUIDL(0); // we are interested about childUidl - if (childUidl != null) { - if (c == null) { - // content has vanished, old content will be removed from - // canvas - // later durin render phase - cc = null; - } else if (cc != null - && cc.getWidget() != client.getPaintable(c)) { - // content has changed - cc = null; - if (widgetToComponentContainer.containsKey(client - .getPaintable(c))) { - // cc exist for this component (moved) use that for this - // cell - cc = widgetToComponentContainer.get(client - .getPaintable(c)); - cc.setWidth(""); - cc.setHeight(""); - } - } - } - childUidl = c; - updateRelSizeStatus(c); - } - - protected void updateRelSizeStatus(UIDL uidl) { - if (uidl != null && !uidl.getBooleanAttribute("cached")) { - if (uidl.hasAttribute("height") - && uidl.getStringAttribute("height").contains("%")) { - relHeight = true; - } else { - relHeight = false; - } - if (uidl.hasAttribute("width")) { - widthCanAffectHeight = relWidth = uidl.getStringAttribute( - "width").contains("%"); - if (uidl.hasAttribute("height")) { - widthCanAffectHeight = false; - } - } else { - widthCanAffectHeight = !uidl.hasAttribute("height"); - relWidth = false; - } - } - } - } - - private Cell getCell(UIDL c) { - int row = c.getIntAttribute("y"); - int col = c.getIntAttribute("x"); - Cell cell = cells[col][row]; - if (cell == null) { - cell = new Cell(c); - cells[col][row] = cell; - } else { - cell.setUidl(c); - } - return cell; - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IHorizontalLayout.java b/src/com/vaadin/terminal/gwt/client/ui/IHorizontalLayout.java deleted file mode 100644 index eeb2c1d1df..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IHorizontalLayout.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.vaadin.terminal.gwt.client.ui; - -public class IHorizontalLayout extends IOrderedLayout { - - public static final String CLASSNAME = "i-horizontallayout"; - - public IHorizontalLayout() { - super(CLASSNAME, ORIENTATION_HORIZONTAL); - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ILabel.java b/src/com/vaadin/terminal/gwt/client/ui/ILabel.java deleted file mode 100644 index c1ef09d8bf..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ILabel.java +++ /dev/null @@ -1,123 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import com.google.gwt.dom.client.Document; -import com.google.gwt.dom.client.Element; -import com.google.gwt.dom.client.NodeList; -import com.google.gwt.dom.client.PreElement; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.HTML; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.ITooltip; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; - -public class ILabel extends HTML implements Paintable { - - public static final String CLASSNAME = "i-label"; - private static final String CLASSNAME_UNDEFINED_WIDTH = "i-label-undef-w"; - - private ApplicationConnection client; - private int verticalPaddingBorder = 0; - private int horizontalPaddingBorder = 0; - - public ILabel() { - super(); - setStyleName(CLASSNAME); - sinkEvents(ITooltip.TOOLTIP_EVENTS); - } - - public ILabel(String text) { - super(text); - setStyleName(CLASSNAME); - sinkEvents(ITooltip.TOOLTIP_EVENTS); - } - - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - if (event.getTypeInt() == Event.ONLOAD) { - Util.notifyParentOfSizeChange(this, true); - event.cancelBubble(true); - return; - } - if (client != null) { - client.handleTooltipEvent(event, this); - } - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - - if (client.updateComponent(this, uidl, true)) { - return; - } - - this.client = client; - - boolean sinkOnloads = false; - - final String mode = uidl.getStringAttribute("mode"); - if (mode == null || "text".equals(mode)) { - setText(uidl.getChildString(0)); - } else if ("pre".equals(mode)) { - PreElement preElement = Document.get().createPreElement(); - preElement.setInnerText(uidl.getChildUIDL(0).getChildString(0)); - // clear existing content - setHTML(""); - // add preformatted text to dom - getElement().appendChild(preElement); - } else if ("uidl".equals(mode)) { - setHTML(uidl.getChildrenAsXML()); - } else if ("xhtml".equals(mode)) { - UIDL content = uidl.getChildUIDL(0).getChildUIDL(0); - if (content.getChildCount() > 0) { - setHTML(content.getChildString(0)); - } else { - setHTML(""); - } - sinkOnloads = true; - } else if ("xml".equals(mode)) { - setHTML(uidl.getChildUIDL(0).getChildString(0)); - } else if ("raw".equals(mode)) { - setHTML(uidl.getChildUIDL(0).getChildString(0)); - sinkOnloads = true; - } else { - setText(""); - } - if (sinkOnloads) { - sinkOnloadsForContainedImgs(); - } - } - - private void sinkOnloadsForContainedImgs() { - NodeList images = getElement().getElementsByTagName("img"); - for (int i = 0; i < images.getLength(); i++) { - Element img = images.getItem(i); - DOM.sinkEvents((com.google.gwt.user.client.Element) img, - Event.ONLOAD); - } - - } - - @Override - public void setHeight(String height) { - verticalPaddingBorder = Util.setHeightExcludingPaddingAndBorder(this, - height, verticalPaddingBorder); - } - - @Override - public void setWidth(String width) { - horizontalPaddingBorder = Util.setWidthExcludingPaddingAndBorder(this, - width, horizontalPaddingBorder); - if (width == null || width.equals("")) { - setStyleName(getElement(), CLASSNAME_UNDEFINED_WIDTH, true); - } else { - setStyleName(getElement(), CLASSNAME_UNDEFINED_WIDTH, false); - } - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ILink.java b/src/com/vaadin/terminal/gwt/client/ui/ILink.java deleted file mode 100644 index 2d09c2186e..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ILink.java +++ /dev/null @@ -1,182 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.ui.ClickListener; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.ITooltip; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; - -public class ILink extends HTML implements Paintable, ClickListener { - - public static final String CLASSNAME = "i-link"; - - private static final int BORDER_STYLE_DEFAULT = 0; - private static final int BORDER_STYLE_MINIMAL = 1; - private static final int BORDER_STYLE_NONE = 2; - - private String src; - - private String target; - - private int borderStyle = BORDER_STYLE_DEFAULT; - - private boolean enabled; - - private boolean readonly; - - private int targetWidth; - - private int targetHeight; - - private Element errorIndicatorElement; - - private final Element anchor = DOM.createAnchor(); - - private final Element captionElement = DOM.createSpan(); - - private Icon icon; - - private ApplicationConnection client; - - public ILink() { - super(); - getElement().appendChild(anchor); - anchor.appendChild(captionElement); - addClickListener(this); - sinkEvents(ITooltip.TOOLTIP_EVENTS); - setStyleName(CLASSNAME); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - - // Ensure correct implementation, - // but don't let container manage caption etc. - if (client.updateComponent(this, uidl, false)) { - return; - } - - this.client = client; - - enabled = uidl.hasAttribute("disabled") ? false : true; - readonly = uidl.hasAttribute("readonly") ? true : false; - - if (uidl.hasAttribute("name")) { - target = uidl.getStringAttribute("name"); - anchor.setAttribute("target", target); - } - if (uidl.hasAttribute("src")) { - src = client.translateToolkitUri(uidl.getStringAttribute("src")); - anchor.setAttribute("href", src); - } - - if (uidl.hasAttribute("border")) { - if ("none".equals(uidl.getStringAttribute("border"))) { - borderStyle = BORDER_STYLE_NONE; - } else { - borderStyle = BORDER_STYLE_MINIMAL; - } - } else { - borderStyle = BORDER_STYLE_DEFAULT; - } - - targetHeight = uidl.hasAttribute("targetHeight") ? uidl - .getIntAttribute("targetHeight") : -1; - targetWidth = uidl.hasAttribute("targetWidth") ? uidl - .getIntAttribute("targetWidth") : -1; - - // Set link caption - captionElement.setInnerText(uidl.getStringAttribute("caption")); - - // handle error - if (uidl.hasAttribute("error")) { - if (errorIndicatorElement == null) { - errorIndicatorElement = DOM.createDiv(); - DOM.setElementProperty(errorIndicatorElement, "className", - "i-errorindicator"); - } - DOM.insertChild(getElement(), errorIndicatorElement, 0); - } else if (errorIndicatorElement != null) { - DOM.setStyleAttribute(errorIndicatorElement, "display", "none"); - } - - if (uidl.hasAttribute("icon")) { - if (icon == null) { - icon = new Icon(client); - anchor.insertBefore(icon.getElement(), captionElement); - } - icon.setUri(uidl.getStringAttribute("icon")); - } - - } - - public void onClick(Widget sender) { - if (enabled && !readonly) { - if (target == null) { - target = "_self"; - } - String features; - switch (borderStyle) { - case BORDER_STYLE_NONE: - features = "menubar=no,location=no,status=no"; - break; - case BORDER_STYLE_MINIMAL: - features = "menubar=yes,location=no,status=no"; - break; - default: - features = ""; - break; - } - - if (targetWidth > 0) { - features += (features.length() > 0 ? "," : "") + "width=" - + targetWidth; - } - if (targetHeight > 0) { - features += (features.length() > 0 ? "," : "") + "height=" - + targetHeight; - } - - if (features.length() > 0) { - // if 'special features' are set, use window.open(), unless - // a modifier key is held (ctrl to open in new tab etc) - Event e = DOM.eventGetCurrentEvent(); - if (!e.getCtrlKey() && !e.getAltKey() && !e.getShiftKey() - && !e.getMetaKey()) { - Window.open(src, target, features); - e.preventDefault(); - } - } - } - } - - @Override - public void onBrowserEvent(Event event) { - final Element target = DOM.eventGetTarget(event); - if (event.getTypeInt() == Event.ONLOAD) { - Util.notifyParentOfSizeChange(this, true); - } - if (client != null) { - client.handleTooltipEvent(event, this); - } - if (target == captionElement || target == anchor - || (icon != null && target == icon.getElement())) { - super.onBrowserEvent(event); - } - if (!enabled) { - event.preventDefault(); - } - - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IListSelect.java b/src/com/vaadin/terminal/gwt/client/ui/IListSelect.java deleted file mode 100644 index 301c7aa65e..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IListSelect.java +++ /dev/null @@ -1,141 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.Iterator; -import java.util.Vector; - -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.ListBox; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.ITooltip; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; - -public class IListSelect extends IOptionGroupBase { - - public static final String CLASSNAME = "i-select"; - - private static final int VISIBLE_COUNT = 10; - - protected TooltipListBox select; - - private int lastSelectedIndex = -1; - - public IListSelect() { - super(new TooltipListBox(true), CLASSNAME); - select = (TooltipListBox) optionsContainer; - select.setSelect(this); - select.addChangeListener(this); - select.addClickListener(this); - select.setStyleName(CLASSNAME + "-select"); - select.setVisibleItemCount(VISIBLE_COUNT); - } - - @Override - protected void buildOptions(UIDL uidl) { - select.setClient(client); - select.setMultipleSelect(isMultiselect()); - select.setEnabled(!isDisabled() && !isReadonly()); - select.clear(); - if (!isMultiselect() && isNullSelectionAllowed() - && !isNullSelectionItemAvailable()) { - // can't unselect last item in singleselect mode - select.addItem("", null); - } - for (final Iterator i = uidl.getChildIterator(); i.hasNext();) { - final UIDL optionUidl = (UIDL) i.next(); - select.addItem(optionUidl.getStringAttribute("caption"), optionUidl - .getStringAttribute("key")); - if (optionUidl.hasAttribute("selected")) { - select.setItemSelected(select.getItemCount() - 1, true); - } - } - if (getRows() > 0) { - select.setVisibleItemCount(getRows()); - } - } - - @Override - protected Object[] getSelectedItems() { - final Vector selectedItemKeys = new Vector(); - for (int i = 0; i < select.getItemCount(); i++) { - if (select.isItemSelected(i)) { - selectedItemKeys.add(select.getValue(i)); - } - } - return selectedItemKeys.toArray(); - } - - @Override - public void onChange(Widget sender) { - final int si = select.getSelectedIndex(); - if (si == -1 && !isNullSelectionAllowed()) { - select.setSelectedIndex(lastSelectedIndex); - } else { - lastSelectedIndex = si; - if (isMultiselect()) { - client.updateVariable(id, "selected", getSelectedItems(), - isImmediate()); - } else { - client.updateVariable(id, "selected", new String[] { "" - + getSelectedItem() }, isImmediate()); - } - } - } - - @Override - public void setHeight(String height) { - select.setHeight(height); - super.setHeight(height); - } - - @Override - public void setWidth(String width) { - select.setWidth(width); - super.setWidth(width); - } - - @Override - protected void setTabIndex(int tabIndex) { - ((TooltipListBox) optionsContainer).setTabIndex(tabIndex); - } - - public void focus() { - select.setFocus(true); - } - -} - -/** - * Extended ListBox to listen tooltip events and forward them to generic - * handler. - */ -class TooltipListBox extends ListBox { - private ApplicationConnection client; - private Paintable pntbl; - - TooltipListBox(boolean isMultiselect) { - super(isMultiselect); - sinkEvents(ITooltip.TOOLTIP_EVENTS); - } - - public void setClient(ApplicationConnection client) { - this.client = client; - } - - public void setSelect(Paintable s) { - pntbl = s; - } - - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - if (client != null) { - client.handleTooltipEvent(event, pntbl); - } - } -} \ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/client/ui/IMarginInfo.java b/src/com/vaadin/terminal/gwt/client/ui/IMarginInfo.java deleted file mode 100644 index 936639356c..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IMarginInfo.java +++ /dev/null @@ -1,76 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.io.Serializable; - -@SuppressWarnings("serial") -public class IMarginInfo implements Serializable { - - private static final int TOP = 1; - private static final int RIGHT = 2; - private static final int BOTTOM = 4; - private static final int LEFT = 8; - - private int bitMask; - - public IMarginInfo(int bitMask) { - this.bitMask = bitMask; - } - - public IMarginInfo(boolean top, boolean right, boolean bottom, boolean left) { - setMargins(top, right, bottom, left); - } - - public void setMargins(boolean top, boolean right, boolean bottom, - boolean left) { - bitMask = top ? TOP : 0; - bitMask += right ? RIGHT : 0; - bitMask += bottom ? BOTTOM : 0; - bitMask += left ? LEFT : 0; - } - - public void setMargins(IMarginInfo marginInfo) { - bitMask = marginInfo.bitMask; - } - - public boolean hasLeft() { - return (bitMask & LEFT) == LEFT; - } - - public boolean hasRight() { - return (bitMask & RIGHT) == RIGHT; - } - - public boolean hasTop() { - return (bitMask & TOP) == TOP; - } - - public boolean hasBottom() { - return (bitMask & BOTTOM) == BOTTOM; - } - - public int getBitMask() { - return bitMask; - } - - public void setMargins(boolean enabled) { - if (enabled) { - bitMask = TOP + RIGHT + BOTTOM + LEFT; - } else { - bitMask = 0; - } - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof IMarginInfo)) { - return false; - } - - return ((IMarginInfo) obj).bitMask == bitMask; - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IMenuBar.java b/src/com/vaadin/terminal/gwt/client/ui/IMenuBar.java deleted file mode 100644 index bb9a3d53e6..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IMenuBar.java +++ /dev/null @@ -1,644 +0,0 @@ -package com.vaadin.terminal.gwt.client.ui; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Stack; - -import com.google.gwt.user.client.Command; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.DeferredCommand; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.HasHTML; -import com.google.gwt.user.client.ui.PopupListener; -import com.google.gwt.user.client.ui.PopupPanel; -import com.google.gwt.user.client.ui.UIObject; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; - -public class IMenuBar extends Widget implements Paintable, PopupListener { - - /** Set the CSS class name to allow styling. */ - public static final String CLASSNAME = "i-menubar"; - - /** For server connections **/ - protected String uidlId; - protected ApplicationConnection client; - - protected final IMenuBar hostReference = this; - protected String submenuIcon = null; - protected boolean collapseItems = true; - protected CustomMenuItem moreItem = null; - - // Construct an empty command to be used when the item has no command - // associated - protected static final Command emptyCommand = null; - - /** Widget fields **/ - protected boolean subMenu; - protected ArrayList items; - protected Element containerElement; - protected IToolkitOverlay popup; - protected IMenuBar visibleChildMenu; - protected IMenuBar parentMenu; - protected CustomMenuItem selected; - - public IMenuBar() { - // Create an empty horizontal menubar - this(false); - } - - public IMenuBar(boolean subMenu) { - super(); - setElement(DOM.createDiv()); - - items = new ArrayList(); - popup = null; - visibleChildMenu = null; - - Element table = DOM.createTable(); - Element tbody = DOM.createTBody(); - DOM.appendChild(getElement(), table); - DOM.appendChild(table, tbody); - - if (!subMenu) { - setStyleName(CLASSNAME); - Element tr = DOM.createTR(); - DOM.appendChild(tbody, tr); - containerElement = tr; - } else { - setStyleName(CLASSNAME + "-submenu"); - containerElement = tbody; - } - this.subMenu = subMenu; - - sinkEvents(Event.ONCLICK | Event.ONMOUSEOVER | Event.ONMOUSEOUT); - } - - /** - * This method must be implemented to update the client-side component from - * UIDL data received from server. - * - * This method is called when the page is loaded for the first time, and - * every time UI changes in the component are received from the server. - */ - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - // This call should be made first. Ensure correct implementation, - // and let the containing layout manage caption, etc. - if (client.updateComponent(this, uidl, true)) { - return; - } - - // For future connections - this.client = client; - uidlId = uidl.getId(); - - // Empty the menu every time it receives new information - if (!getItems().isEmpty()) { - clearItems(); - } - - UIDL options = uidl.getChildUIDL(0); - - if (options.hasAttribute("submenuIcon")) { - submenuIcon = client.translateToolkitUri(uidl.getChildUIDL(0) - .getStringAttribute("submenuIcon")); - } else { - submenuIcon = null; - } - - collapseItems = options.getBooleanAttribute("collapseItems"); - - if (collapseItems) { - UIDL moreItemUIDL = options.getChildUIDL(0); - StringBuffer itemHTML = new StringBuffer(); - - if (moreItemUIDL.hasAttribute("icon")) { - itemHTML.append(""); - } - itemHTML.append(moreItemUIDL.getStringAttribute("text")); - - moreItem = new CustomMenuItem(itemHTML.toString(), emptyCommand); - } - - UIDL uidlItems = uidl.getChildUIDL(1); - Iterator itr = uidlItems.getChildIterator(); - Stack> iteratorStack = new Stack>(); - Stack menuStack = new Stack(); - IMenuBar currentMenu = this; - - while (itr.hasNext()) { - UIDL item = (UIDL) itr.next(); - CustomMenuItem currentItem = null; - - String itemText = item.getStringAttribute("text"); - final int itemId = item.getIntAttribute("id"); - - boolean itemHasCommand = item.getBooleanAttribute("command"); - - // Construct html from the text and the optional icon - StringBuffer itemHTML = new StringBuffer(); - - if (item.hasAttribute("icon")) { - itemHTML.append(""); - } - - itemHTML.append(itemText); - - if (currentMenu != this && item.getChildCount() > 0 - && submenuIcon != null) { - itemHTML.append(""); - } - - Command cmd = null; - - if (itemHasCommand) { - // Construct a command that fires onMenuClick(int) with the - // item's id-number - cmd = new Command() { - public void execute() { - hostReference.onMenuClick(itemId); - } - }; - } - - currentItem = currentMenu.addItem(itemHTML.toString(), cmd); - - if (item.getChildCount() > 0) { - menuStack.push(currentMenu); - iteratorStack.push(itr); - itr = item.getChildIterator(); - currentMenu = new IMenuBar(true); - currentItem.setSubMenu(currentMenu); - } - - while (!itr.hasNext() && !iteratorStack.empty()) { - itr = iteratorStack.pop(); - currentMenu = (IMenuBar) menuStack.pop(); - } - }// while - - // we might need to collapse the top-level menu - if (collapseItems) { - int topLevelWidth = 0; - - int ourWidth = getOffsetWidth(); - - int i = 0; - for (; i < getItems().size() && topLevelWidth < ourWidth; i++) { - CustomMenuItem item = (CustomMenuItem) getItems().get(i); - topLevelWidth += item.getOffsetWidth(); - } - - if (topLevelWidth > getOffsetWidth()) { - ArrayList toBeCollapsed = new ArrayList(); - IMenuBar collapsed = new IMenuBar(true); - for (int j = i - 2; j < getItems().size(); j++) { - toBeCollapsed.add(getItems().get(j)); - } - - for (int j = 0; j < toBeCollapsed.size(); j++) { - CustomMenuItem item = (CustomMenuItem) toBeCollapsed.get(j); - removeItem(item); - - // it's ugly, but we have to insert the submenu icon - if (item.getSubMenu() != null && submenuIcon != null) { - StringBuffer itemText = new StringBuffer(item.getHTML()); - itemText.append(""); - item.setHTML(itemText.toString()); - } - - collapsed.addItem(item); - } - - moreItem.setSubMenu(collapsed); - addItem(moreItem); - } - } - }// updateFromUIDL - - /** - * This is called by the items in the menu and it communicates the - * information to the server - * - * @param clickedItemId - * id of the item that was clicked - */ - public void onMenuClick(int clickedItemId) { - // Updating the state to the server can not be done before - // the server connection is known, i.e., before updateFromUIDL() - // has been called. - if (uidlId != null && client != null) { - // Communicate the user interaction parameters to server. This call - // will initiate an AJAX request to the server. - client.updateVariable(uidlId, "clickedId", clickedItemId, true); - } - } - - /** Widget methods **/ - - /** - * Returns a list of items in this menu - */ - public List getItems() { - return items; - } - - /** - * Remove all the items in this menu - */ - public void clearItems() { - Element e = getContainingElement(); - while (DOM.getChildCount(e) > 0) { - DOM.removeChild(e, DOM.getChild(e, 0)); - } - items.clear(); - } - - /** - * Returns the containing element of the menu - * - * @return - */ - public Element getContainingElement() { - return containerElement; - } - - /** - * Returns a new child element to add an item to - * - * @return - */ - public Element getNewChildElement() { - if (subMenu) { - Element tr = DOM.createTR(); - DOM.appendChild(getContainingElement(), tr); - return tr; - } else { - return getContainingElement(); - } - - } - - /** - * Add a new item to this menu - * - * @param html - * items text - * @param cmd - * items command - * @return the item created - */ - public CustomMenuItem addItem(String html, Command cmd) { - CustomMenuItem item = new CustomMenuItem(html, cmd); - addItem(item); - return item; - } - - /** - * Add a new item to this menu - * - * @param item - */ - public void addItem(CustomMenuItem item) { - DOM.appendChild(getNewChildElement(), item.getElement()); - item.setParentMenu(this); - item.setSelected(false); - items.add(item); - } - - /** - * Remove the given item from this menu - * - * @param item - */ - public void removeItem(CustomMenuItem item) { - if (items.contains(item)) { - int index = items.indexOf(item); - Element container = getContainingElement(); - - DOM.removeChild(container, DOM.getChild(container, index)); - items.remove(index); - } - } - - /* - * @see - * com.google.gwt.user.client.ui.Widget#onBrowserEvent(com.google.gwt.user - * .client.Event) - */ - @Override - public void onBrowserEvent(Event e) { - super.onBrowserEvent(e); - - Element targetElement = DOM.eventGetTarget(e); - CustomMenuItem targetItem = null; - for (int i = 0; i < items.size(); i++) { - CustomMenuItem item = (CustomMenuItem) items.get(i); - if (DOM.isOrHasChild(item.getElement(), targetElement)) { - targetItem = item; - } - } - - if (targetItem != null) { - switch (DOM.eventGetType(e)) { - - case Event.ONCLICK: - itemClick(targetItem); - break; - - case Event.ONMOUSEOVER: - itemOver(targetItem); - break; - - case Event.ONMOUSEOUT: - itemOut(targetItem); - break; - } - } - } - - /** - * When an item is clicked - * - * @param item - */ - public void itemClick(CustomMenuItem item) { - if (item.getCommand() != null) { - setSelected(null); - - if (visibleChildMenu != null) { - visibleChildMenu.hideChildren(); - } - - hideParents(); - DeferredCommand.addCommand(item.getCommand()); - - } else { - if (item.getSubMenu() != null - && item.getSubMenu() != visibleChildMenu) { - setSelected(item); - showChildMenu(item); - } - } - } - - /** - * When the user hovers the mouse over the item - * - * @param item - */ - public void itemOver(CustomMenuItem item) { - setSelected(item); - - boolean menuWasVisible = visibleChildMenu != null; - - if (menuWasVisible && visibleChildMenu != item.getSubMenu()) { - popup.hide(); - visibleChildMenu = null; - } - - if (item.getSubMenu() != null && (parentMenu != null || menuWasVisible) - && visibleChildMenu != item.getSubMenu()) { - showChildMenu(item); - } - } - - /** - * When the mouse is moved away from an item - * - * @param item - */ - public void itemOut(CustomMenuItem item) { - if (visibleChildMenu != item.getSubMenu() || visibleChildMenu == null) { - hideChildMenu(item); - setSelected(null); - } - } - - /** - * Shows the child menu of an item. The caller must ensure that the item has - * a submenu. - * - * @param item - */ - public void showChildMenu(CustomMenuItem item) { - popup = new IToolkitOverlay(true, false, true); - popup.setWidget(item.getSubMenu()); - popup.addPopupListener(this); - - if (subMenu) { - popup.setPopupPosition(item.getParentMenu().getAbsoluteLeft() - + item.getParentMenu().getOffsetWidth(), item - .getAbsoluteTop()); - } else { - popup.setPopupPosition(item.getAbsoluteLeft(), item.getParentMenu() - .getAbsoluteTop() - + item.getParentMenu().getOffsetHeight()); - } - - item.getSubMenu().onShow(); - visibleChildMenu = item.getSubMenu(); - item.getSubMenu().setParentMenu(this); - - popup.show(); - } - - /** - * Hides the submenu of an item - * - * @param item - */ - public void hideChildMenu(CustomMenuItem item) { - if (visibleChildMenu != null - && !(visibleChildMenu == item.getSubMenu())) { - popup.hide(); - - } - } - - /** - * When the menu is shown. - */ - public void onShow() { - if (!items.isEmpty()) { - ((CustomMenuItem) items.get(0)).setSelected(true); - } - } - - /** - * Recursively hide all child menus - */ - public void hideChildren() { - if (visibleChildMenu != null) { - visibleChildMenu.hideChildren(); - popup.hide(); - } - } - - /** - * Recursively hide all parent menus - */ - public void hideParents() { - - if (visibleChildMenu != null) { - popup.hide(); - setSelected(null); - } - - if (getParentMenu() != null) { - getParentMenu().hideParents(); - } - } - - /** - * Returns the parent menu of this menu, or null if this is the top-level - * menu - * - * @return - */ - public IMenuBar getParentMenu() { - return parentMenu; - } - - /** - * Set the parent menu of this menu - * - * @param parent - */ - public void setParentMenu(IMenuBar parent) { - parentMenu = parent; - } - - /** - * Returns the currently selected item of this menu, or null if nothing is - * selected - * - * @return - */ - public CustomMenuItem getSelected() { - return selected; - } - - /** - * Set the currently selected item of this menu - * - * @param item - */ - public void setSelected(CustomMenuItem item) { - // If we had something selected, unselect - if (item != selected && selected != null) { - selected.setSelected(false); - } - // If we have a valid selection, select it - if (item != null) { - item.setSelected(true); - } - - selected = item; - } - - /** - * Listener method, fired when this menu is closed - */ - public void onPopupClosed(PopupPanel sender, boolean autoClosed) { - hideChildren(); - if (autoClosed) { - hideParents(); - } - // setSelected(null); - visibleChildMenu = null; - popup = null; - - } - - /** - * - * A class to hold information on menu items - * - */ - private class CustomMenuItem extends UIObject implements HasHTML { - - protected String html = null; - protected Command command = null; - protected IMenuBar subMenu = null; - protected IMenuBar parentMenu = null; - - public CustomMenuItem(String html, Command cmd) { - setElement(DOM.createTD()); - - setHTML(html); - setCommand(cmd); - setSelected(false); - - addStyleName("menuitem"); - } - - public void setSelected(boolean selected) { - if (selected) { - addStyleDependentName("selected"); - } else { - removeStyleDependentName("selected"); - } - } - - /* - * setters and getters for the fields - */ - - public void setSubMenu(IMenuBar subMenu) { - this.subMenu = subMenu; - } - - public IMenuBar getSubMenu() { - return subMenu; - } - - public void setParentMenu(IMenuBar parentMenu) { - this.parentMenu = parentMenu; - } - - public IMenuBar getParentMenu() { - return parentMenu; - } - - public void setCommand(Command command) { - this.command = command; - } - - public Command getCommand() { - return command; - } - - public String getHTML() { - return html; - } - - public void setHTML(String html) { - this.html = html; - DOM.setInnerHTML(getElement(), html); - } - - public String getText() { - return html; - } - - public void setText(String text) { - setHTML(text); - - } - } - -}// class IMenuBar diff --git a/src/com/vaadin/terminal/gwt/client/ui/INativeSelect.java b/src/com/vaadin/terminal/gwt/client/ui/INativeSelect.java deleted file mode 100644 index 3b063bb5bf..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/INativeSelect.java +++ /dev/null @@ -1,111 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.Iterator; -import java.util.Vector; - -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; - -public class INativeSelect extends IOptionGroupBase implements Field { - - public static final String CLASSNAME = "i-select"; - - protected TooltipListBox select; - - public INativeSelect() { - super(new TooltipListBox(false), CLASSNAME); - select = (TooltipListBox) optionsContainer; - select.setSelect(this); - select.setVisibleItemCount(1); - select.addChangeListener(this); - select.setStyleName(CLASSNAME + "-select"); - - } - - @Override - protected void buildOptions(UIDL uidl) { - select.setClient(client); - select.setEnabled(!isDisabled() && !isReadonly()); - select.clear(); - if (isNullSelectionAllowed() && !isNullSelectionItemAvailable()) { - // can't unselect last item in singleselect mode - select.addItem("", null); - } - boolean selected = false; - for (final Iterator i = uidl.getChildIterator(); i.hasNext();) { - final UIDL optionUidl = (UIDL) i.next(); - select.addItem(optionUidl.getStringAttribute("caption"), optionUidl - .getStringAttribute("key")); - if (optionUidl.hasAttribute("selected")) { - select.setItemSelected(select.getItemCount() - 1, true); - selected = true; - } - } - if (!selected && !isNullSelectionAllowed()) { - // null-select not allowed, but value not selected yet; add null and - // remove when something is selected - select.insertItem("", null, 0); - select.setItemSelected(0, true); - } - if (BrowserInfo.get().isIE6()) { - // lazy size change - IE6 uses naive dropdown that does not have a - // proper size yet - Util.notifyParentOfSizeChange(this, true); - } - } - - @Override - protected Object[] getSelectedItems() { - final Vector selectedItemKeys = new Vector(); - for (int i = 0; i < select.getItemCount(); i++) { - if (select.isItemSelected(i)) { - selectedItemKeys.add(select.getValue(i)); - } - } - return selectedItemKeys.toArray(); - } - - @Override - public void onChange(Widget sender) { - - if (select.isMultipleSelect()) { - client.updateVariable(id, "selected", getSelectedItems(), - isImmediate()); - } else { - client.updateVariable(id, "selected", new String[] { "" - + getSelectedItem() }, isImmediate()); - } - if (!isNullSelectionAllowed() && "null".equals(select.getValue(0))) { - // remove temporary empty item - select.removeItem(0); - } - } - - @Override - public void setHeight(String height) { - select.setHeight(height); - super.setHeight(height); - } - - @Override - public void setWidth(String width) { - select.setWidth(width); - super.setWidth(width); - } - - @Override - protected void setTabIndex(int tabIndex) { - ((TooltipListBox) optionsContainer).setTabIndex(tabIndex); - } - - public void focus() { - select.setFocus(true); - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/INotification.java b/src/com/vaadin/terminal/gwt/client/ui/INotification.java deleted file mode 100644 index 5ebb6c710e..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/INotification.java +++ /dev/null @@ -1,323 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.ArrayList; -import java.util.Date; -import java.util.EventObject; -import java.util.Iterator; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.BrowserInfo; - -public class INotification extends IToolkitOverlay { - - public static final int CENTERED = 1; - public static final int CENTERED_TOP = 2; - public static final int CENTERED_BOTTOM = 3; - public static final int TOP_LEFT = 4; - public static final int TOP_RIGHT = 5; - public static final int BOTTOM_LEFT = 6; - public static final int BOTTOM_RIGHT = 7; - - public static final int DELAY_FOREVER = -1; - public static final int DELAY_NONE = 0; - - private static final String STYLENAME = "i-Notification"; - private static final int mouseMoveThreshold = 7; - private static final int Z_INDEX_BASE = 20000; - public static final String STYLE_SYSTEM = "system"; - private static final int FADE_ANIMATION_INTERVAL = 50; // == 20 fps - - private int startOpacity = 90; - private int fadeMsec = 400; - private int delayMsec = 1000; - - private Timer fader; - private Timer delay; - - private int x = -1; - private int y = -1; - - private String temporaryStyle; - - private ArrayList listeners; - - public INotification() { - setStylePrimaryName(STYLENAME); - sinkEvents(Event.ONCLICK); - DOM.setStyleAttribute(getElement(), "zIndex", "" + Z_INDEX_BASE); - } - - public INotification(int delayMsec) { - this(); - this.delayMsec = delayMsec; - } - - public INotification(int delayMsec, int fadeMsec, int startOpacity) { - this(delayMsec); - this.fadeMsec = fadeMsec; - this.startOpacity = startOpacity; - } - - public void startDelay() { - DOM.removeEventPreview(this); - if (delayMsec > 0) { - if (delay == null) { - delay = new Timer() { - @Override - public void run() { - fade(); - } - }; - delay.schedule(delayMsec); - } - } else if (delayMsec == 0) { - fade(); - } - } - - @Override - public void show() { - show(CENTERED); - } - - public void show(String style) { - show(CENTERED, style); - } - - public void show(int position) { - show(position, null); - } - - public void show(Widget widget, int position, String style) { - setWidget(widget); - show(position, style); - } - - public void show(String html, int position, String style) { - setWidget(new HTML(html)); - show(position, style); - } - - public void show(int position, String style) { - setOpacity(getElement(), startOpacity); - if (style != null) { - temporaryStyle = style; - addStyleName(style); - } - super.show(); - setPosition(position); - } - - @Override - public void hide() { - DOM.removeEventPreview(this); - cancelDelay(); - cancelFade(); - if (temporaryStyle != null) { - removeStyleName(temporaryStyle); - temporaryStyle = null; - } - super.hide(); - fireEvent(new HideEvent(this)); - } - - public void fade() { - DOM.removeEventPreview(this); - cancelDelay(); - fader = new Timer() { - private final long start = new Date().getTime(); - - @Override - public void run() { - /* - * To make animation smooth, don't count that event happens on - * time. Reduce opacity according to the actual time spent - * instead of fixed decrement. - */ - long now = new Date().getTime(); - long timeEplaced = now - start; - float remainingFraction = 1 - timeEplaced / (float) fadeMsec; - int opacity = (int) (startOpacity * remainingFraction); - if (opacity <= 0) { - cancel(); - hide(); - if (BrowserInfo.get().isOpera()) { - // tray notification on opera needs to explicitly define - // size, reset it - DOM.setStyleAttribute(getElement(), "width", ""); - DOM.setStyleAttribute(getElement(), "height", ""); - } - } else { - setOpacity(getElement(), opacity); - } - } - }; - fader.scheduleRepeating(FADE_ANIMATION_INTERVAL); - } - - public void setPosition(int position) { - final Element el = getElement(); - DOM.setStyleAttribute(el, "top", ""); - DOM.setStyleAttribute(el, "left", ""); - DOM.setStyleAttribute(el, "bottom", ""); - DOM.setStyleAttribute(el, "right", ""); - switch (position) { - case TOP_LEFT: - DOM.setStyleAttribute(el, "top", "0px"); - DOM.setStyleAttribute(el, "left", "0px"); - break; - case TOP_RIGHT: - DOM.setStyleAttribute(el, "top", "0px"); - DOM.setStyleAttribute(el, "right", "0px"); - break; - case BOTTOM_RIGHT: - DOM.setStyleAttribute(el, "position", "absolute"); - if (BrowserInfo.get().isOpera()) { - // tray notification on opera needs explicitly defined size - DOM.setStyleAttribute(el, "width", getOffsetWidth() + "px"); - DOM.setStyleAttribute(el, "height", getOffsetHeight() + "px"); - } - DOM.setStyleAttribute(el, "bottom", "0px"); - DOM.setStyleAttribute(el, "right", "0px"); - break; - case BOTTOM_LEFT: - DOM.setStyleAttribute(el, "bottom", "0px"); - DOM.setStyleAttribute(el, "left", "0px"); - break; - case CENTERED_TOP: - center(); - DOM.setStyleAttribute(el, "top", "0px"); - break; - case CENTERED_BOTTOM: - center(); - DOM.setStyleAttribute(el, "top", ""); - DOM.setStyleAttribute(el, "bottom", "0px"); - break; - default: - case CENTERED: - center(); - break; - } - } - - private void cancelFade() { - if (fader != null) { - fader.cancel(); - fader = null; - } - } - - private void cancelDelay() { - if (delay != null) { - delay.cancel(); - delay = null; - } - } - - private void setOpacity(Element el, int opacity) { - DOM.setStyleAttribute(el, "opacity", "" + (opacity / 100.0)); - if (BrowserInfo.get().isIE()) { - DOM.setStyleAttribute(el, "filter", "Alpha(opacity=" + opacity - + ")"); - } - } - - @Override - public void onBrowserEvent(Event event) { - DOM.removeEventPreview(this); - if (fader == null) { - fade(); - } - } - - @Override - public boolean onEventPreview(Event event) { - int type = DOM.eventGetType(event); - // "modal" - if (delayMsec == -1 || temporaryStyle == STYLE_SYSTEM) { - if (type == Event.ONCLICK) { - if (DOM.isOrHasChild(getElement(), DOM.eventGetTarget(event))) { - fade(); - return false; - } - } - if (temporaryStyle == STYLE_SYSTEM) { - return true; - } else { - return false; - } - } - // default - switch (type) { - case Event.ONMOUSEMOVE: - - if (x < 0) { - x = DOM.eventGetClientX(event); - y = DOM.eventGetClientY(event); - } else if (Math.abs(DOM.eventGetClientX(event) - x) > mouseMoveThreshold - || Math.abs(DOM.eventGetClientY(event) - y) > mouseMoveThreshold) { - startDelay(); - } - break; - case Event.ONMOUSEDOWN: - case Event.ONMOUSEWHEEL: - case Event.ONSCROLL: - startDelay(); - break; - case Event.ONKEYDOWN: - if (event.getRepeat()) { - return true; - } - startDelay(); - break; - default: - break; - } - return true; - } - - public void addEventListener(EventListener listener) { - if (listeners == null) { - listeners = new ArrayList(); - } - listeners.add(listener); - } - - public void removeEventListener(EventListener listener) { - if (listeners == null) { - return; - } - listeners.remove(listener); - } - - private void fireEvent(HideEvent event) { - if (listeners != null) { - for (Iterator it = listeners.iterator(); it - .hasNext();) { - EventListener l = it.next(); - l.notificationHidden(event); - } - } - } - - public class HideEvent extends EventObject { - private static final long serialVersionUID = 4428671753988459560L; - - public HideEvent(Object source) { - super(source); - } - } - - public interface EventListener extends java.util.EventListener { - public void notificationHidden(HideEvent event); - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IOptionGroup.java b/src/com/vaadin/terminal/gwt/client/ui/IOptionGroup.java deleted file mode 100644 index cec3d07be8..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IOptionGroup.java +++ /dev/null @@ -1,102 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import com.google.gwt.user.client.ui.CheckBox; -import com.google.gwt.user.client.ui.HasFocus; -import com.google.gwt.user.client.ui.Panel; -import com.google.gwt.user.client.ui.RadioButton; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.UIDL; - -public class IOptionGroup extends IOptionGroupBase { - - public static final String CLASSNAME = "i-select-optiongroup"; - - private final Panel panel; - - private final Map optionsToKeys; - - public IOptionGroup() { - super(CLASSNAME); - panel = (Panel) optionsContainer; - optionsToKeys = new HashMap(); - } - - /* - * Return true if no elements were changed, false otherwise. - */ - @Override - protected void buildOptions(UIDL uidl) { - panel.clear(); - for (final Iterator it = uidl.getChildIterator(); it.hasNext();) { - final UIDL opUidl = (UIDL) it.next(); - CheckBox op; - if (isMultiselect()) { - op = new ICheckBox(); - op.setText(opUidl.getStringAttribute("caption")); - } else { - op = new RadioButton(id, opUidl.getStringAttribute("caption")); - op.setStyleName("i-radiobutton"); - } - op.addStyleName(CLASSNAME_OPTION); - op.setChecked(opUidl.getBooleanAttribute("selected")); - op.setEnabled(!opUidl.getBooleanAttribute("disabled") - && !isReadonly() && !isDisabled()); - op.addClickListener(this); - optionsToKeys.put(op, opUidl.getStringAttribute("key")); - panel.add(op); - } - } - - @Override - protected Object[] getSelectedItems() { - return selectedKeys.toArray(); - } - - @Override - public void onClick(Widget sender) { - super.onClick(sender); - if (sender instanceof CheckBox) { - final boolean selected = ((CheckBox) sender).isChecked(); - final String key = (String) optionsToKeys.get(sender); - if (!isMultiselect()) { - selectedKeys.clear(); - } - if (selected) { - selectedKeys.add(key); - } else { - selectedKeys.remove(key); - } - client.updateVariable(id, "selected", getSelectedItems(), - isImmediate()); - } - } - - @Override - protected void setTabIndex(int tabIndex) { - for (Iterator iterator = panel.iterator(); iterator.hasNext();) { - if (isMultiselect()) { - ICheckBox cb = (ICheckBox) iterator.next(); - cb.setTabIndex(tabIndex); - } else { - RadioButton rb = (RadioButton) iterator.next(); - rb.setTabIndex(tabIndex); - } - } - } - - public void focus() { - Iterator iterator = panel.iterator(); - if (iterator.hasNext()) { - ((HasFocus) iterator.next()).setFocus(true); - } - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IOptionGroupBase.java b/src/com/vaadin/terminal/gwt/client/ui/IOptionGroupBase.java deleted file mode 100644 index d91ade2639..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IOptionGroupBase.java +++ /dev/null @@ -1,229 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.Set; - -import com.google.gwt.user.client.ui.ChangeListener; -import com.google.gwt.user.client.ui.ClickListener; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.KeyboardListener; -import com.google.gwt.user.client.ui.Panel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.Focusable; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; - -abstract class IOptionGroupBase extends Composite implements Paintable, Field, - ClickListener, ChangeListener, KeyboardListener, Focusable { - - public static final String CLASSNAME_OPTION = "i-select-option"; - - protected ApplicationConnection client; - - protected String id; - - protected Set selectedKeys; - - private boolean immediate; - - private boolean multiselect; - - private boolean disabled; - - private boolean readonly; - - private int cols = 0; - - private int rows = 0; - - private boolean nullSelectionAllowed = true; - - private boolean nullSelectionItemAvailable = false; - - /** - * Widget holding the different options (e.g. ListBox or Panel for radio - * buttons) (optional, fallbacks to container Panel) - */ - protected Widget optionsContainer; - - /** - * Panel containing the component - */ - private final Panel container; - - private ITextField newItemField; - - private IButton newItemButton; - - public IOptionGroupBase(String classname) { - container = new FlowPanel(); - initWidget(container); - optionsContainer = container; - container.setStyleName(classname); - immediate = false; - multiselect = false; - } - - /* - * Call this if you wish to specify your own container for the option - * elements (e.g. SELECT) - */ - public IOptionGroupBase(Widget w, String classname) { - this(classname); - optionsContainer = w; - container.add(optionsContainer); - } - - protected boolean isImmediate() { - return immediate; - } - - protected boolean isMultiselect() { - return multiselect; - } - - protected boolean isDisabled() { - return disabled; - } - - protected boolean isReadonly() { - return readonly; - } - - protected boolean isNullSelectionAllowed() { - return nullSelectionAllowed; - } - - protected boolean isNullSelectionItemAvailable() { - return nullSelectionItemAvailable; - } - - /** - * @return "cols" specified in uidl, 0 if not specified - */ - protected int getColumns() { - return cols; - } - - /** - * @return "rows" specified in uidl, 0 if not specified - */ - - protected int getRows() { - return rows; - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - this.client = client; - id = uidl.getId(); - - if (client.updateComponent(this, uidl, true)) { - return; - } - - selectedKeys = uidl.getStringArrayVariableAsSet("selected"); - - readonly = uidl.getBooleanAttribute("readonly"); - disabled = uidl.getBooleanAttribute("disabled"); - multiselect = "multi".equals(uidl.getStringAttribute("selectmode")); - immediate = uidl.getBooleanAttribute("immediate"); - nullSelectionAllowed = uidl.getBooleanAttribute("nullselect"); - nullSelectionItemAvailable = uidl.getBooleanAttribute("nullselectitem"); - - cols = uidl.getIntAttribute("cols"); - rows = uidl.getIntAttribute("rows"); - - final UIDL ops = uidl.getChildUIDL(0); - - if (getColumns() > 0) { - container.setWidth(getColumns() + "em"); - if (container != optionsContainer) { - optionsContainer.setWidth("100%"); - } - } - - buildOptions(ops); - - if (uidl.getBooleanAttribute("allownewitem")) { - if (newItemField == null) { - newItemButton = new IButton(); - newItemButton.setText("+"); - newItemButton.setWidth("1.5em"); - newItemButton.addClickListener(this); - newItemField = new ITextField(); - newItemField.addKeyboardListener(this); - // newItemField.setColumns(16); - if (getColumns() > 0) { - newItemField.setWidth((getColumns() - 2) + "em"); - } - } - newItemField.setEnabled(!disabled && !readonly); - newItemButton.setEnabled(!disabled && !readonly); - - if (newItemField == null || newItemField.getParent() != container) { - container.add(newItemField); - container.add(newItemButton); - } - } else if (newItemField != null) { - container.remove(newItemField); - container.remove(newItemButton); - } - - setTabIndex(uidl.hasAttribute("tabindex") ? uidl - .getIntAttribute("tabindex") : 0); - - } - - abstract protected void setTabIndex(int tabIndex); - - public void onClick(Widget sender) { - if (sender == newItemButton && !newItemField.getText().equals("")) { - client.updateVariable(id, "newitem", newItemField.getText(), true); - newItemField.setText(""); - } - } - - public void onChange(Widget sender) { - if (multiselect) { - client - .updateVariable(id, "selected", getSelectedItems(), - immediate); - } else { - client.updateVariable(id, "selected", new String[] { "" - + getSelectedItem() }, immediate); - } - } - - public void onKeyPress(Widget sender, char keyCode, int modifiers) { - if (sender == newItemField && keyCode == KeyboardListener.KEY_ENTER) { - newItemButton.click(); - } - } - - public void onKeyUp(Widget sender, char keyCode, int modifiers) { - // Ignore, subclasses may override - } - - public void onKeyDown(Widget sender, char keyCode, int modifiers) { - // Ignore, subclasses may override - } - - protected abstract void buildOptions(UIDL uidl); - - protected abstract Object[] getSelectedItems(); - - protected Object getSelectedItem() { - final Object[] sel = getSelectedItems(); - if (sel.length > 0) { - return sel[0]; - } else { - return null; - } - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IOrderedLayout.java b/src/com/vaadin/terminal/gwt/client/ui/IOrderedLayout.java deleted file mode 100644 index 305b930351..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IOrderedLayout.java +++ /dev/null @@ -1,876 +0,0 @@ -package com.vaadin.terminal.gwt.client.ui; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Set; - -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.RenderSpace; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; -import com.vaadin.terminal.gwt.client.RenderInformation.FloatSize; -import com.vaadin.terminal.gwt.client.RenderInformation.Size; -import com.vaadin.terminal.gwt.client.ui.layout.CellBasedLayout; -import com.vaadin.terminal.gwt.client.ui.layout.ChildComponentContainer; - -public class IOrderedLayout extends CellBasedLayout { - - public static final String CLASSNAME = "i-orderedlayout"; - - private int orientation; - - // Can be removed once OrderedLayout is removed - private boolean allowOrientationUpdate = false; - - /** - * Size of the layout excluding any margins. - */ - private Size activeLayoutSize = new Size(0, 0); - - private boolean isRendering = false; - - private String width = ""; - - private boolean sizeHasChangedDuringRendering = false; - - public IOrderedLayout() { - this(CLASSNAME, ORIENTATION_VERTICAL); - allowOrientationUpdate = true; - } - - protected IOrderedLayout(String className, int orientation) { - setStyleName(className); - this.orientation = orientation; - - STYLENAME_SPACING = className + "-spacing"; - STYLENAME_MARGIN_TOP = className + "-margin-top"; - STYLENAME_MARGIN_RIGHT = className + "-margin-right"; - STYLENAME_MARGIN_BOTTOM = className + "-margin-bottom"; - STYLENAME_MARGIN_LEFT = className + "-margin-left"; - - } - - @Override - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - isRendering = true; - super.updateFromUIDL(uidl, client); - - // Only non-cached, visible UIDL:s can introduce changes - if (uidl.getBooleanAttribute("cached") - || uidl.getBooleanAttribute("invisible")) { - isRendering = false; - return; - } - - if (allowOrientationUpdate) { - handleOrientationUpdate(uidl); - } - - // IStopWatch w = new IStopWatch("OrderedLayout.updateFromUIDL"); - - ArrayList uidlWidgets = new ArrayList(uidl - .getChildCount()); - ArrayList relativeSizeComponents = new ArrayList(); - ArrayList relativeSizeComponentUIDL = new ArrayList(); - - int pos = 0; - for (final Iterator it = uidl.getChildIterator(); it.hasNext();) { - final UIDL childUIDL = it.next(); - final Paintable child = client.getPaintable(childUIDL); - Widget widget = (Widget) child; - - // Create container for component - ChildComponentContainer childComponentContainer = getComponentContainer(widget); - - if (childComponentContainer == null) { - // This is a new component - childComponentContainer = createChildContainer(widget); - } - - addOrMoveChild(childComponentContainer, pos++); - - /* - * Components which are to be expanded in the same orientation as - * the layout are rendered later when it is clear how much space - * they can use - */ - if (!Util.isCached(childUIDL)) { - FloatSize relativeSize = Util.parseRelativeSize(childUIDL); - childComponentContainer.setRelativeSize(relativeSize); - } - - if (childComponentContainer.isComponentRelativeSized(orientation)) { - relativeSizeComponents.add(childComponentContainer); - relativeSizeComponentUIDL.add(childUIDL); - } else { - if (isDynamicWidth()) { - childComponentContainer.renderChild(childUIDL, client, 0); - } else { - childComponentContainer.renderChild(childUIDL, client, - activeLayoutSize.getWidth()); - } - if (sizeHasChangedDuringRendering && Util.isCached(childUIDL)) { - // notify cached relative sized component about size - // chance - client.handleComponentRelativeSize(childComponentContainer - .getWidget()); - } - } - - uidlWidgets.add(widget); - - } - - // w.mark("Rendering of " - // + (uidlWidgets.size() - relativeSizeComponents.size()) - // + " absolute size components done"); - - /* - * Remove any children after pos. These are the ones that previously - * were in the layout but have now been removed - */ - removeChildrenAfter(pos); - - // w.mark("Old children removed"); - - /* Fetch alignments and expand ratio from UIDL */ - updateAlignmentsAndExpandRatios(uidl, uidlWidgets); - // w.mark("Alignments and expand ratios updated"); - - /* Fetch widget sizes from rendered components */ - updateWidgetSizes(); - // w.mark("Widget sizes updated"); - - recalculateLayout(); - // w.mark("Layout size calculated (" + activeLayoutSize + - // ") offsetSize: " - // + getOffsetWidth() + "," + getOffsetHeight()); - - /* Render relative size components */ - for (int i = 0; i < relativeSizeComponents.size(); i++) { - ChildComponentContainer childComponentContainer = relativeSizeComponents - .get(i); - UIDL childUIDL = relativeSizeComponentUIDL.get(i); - - if (isDynamicWidth()) { - childComponentContainer.renderChild(childUIDL, client, 0); - } else { - childComponentContainer.renderChild(childUIDL, client, - activeLayoutSize.getWidth()); - } - - if (Util.isCached(childUIDL)) { - /* - * We must update the size of the relative sized component if - * the expand ratio or something else in the layout changes - * which affects the size of a relative sized component - */ - client.handleComponentRelativeSize(childComponentContainer - .getWidget()); - } - - // childComponentContainer.updateWidgetSize(); - } - - // w.mark("Rendering of " + (relativeSizeComponents.size()) - // + " relative size components done"); - - /* Fetch widget sizes for relative size components */ - for (ChildComponentContainer childComponentContainer : widgetToComponentContainer - .values()) { - - /* Update widget size from DOM */ - childComponentContainer.updateWidgetSize(); - } - - // w.mark("Widget sizes updated"); - - /* - * Components with relative size in main direction may affect the layout - * size in the other direction - */ - if ((isHorizontal() && isDynamicHeight()) - || (isVertical() && isDynamicWidth())) { - layoutSizeMightHaveChanged(); - } - // w.mark("Layout dimensions updated"); - - /* Update component spacing */ - updateContainerMargins(); - - /* - * Update component sizes for components with relative size in non-main - * direction - */ - if (updateRelativeSizesInNonMainDirection()) { - // Sizes updated - might affect the other dimension so we need to - // recheck the widget sizes and recalculate layout dimensions - updateWidgetSizes(); - layoutSizeMightHaveChanged(); - } - calculateAlignments(); - // w.mark("recalculateComponentSizesAndAlignments done"); - - setRootSize(); - - if (BrowserInfo.get().isIE()) { - /* - * This should fix the issue with padding not always taken into - * account for the containers leading to no spacing between - * elements. - */ - root.getStyle().setProperty("zoom", "1"); - } - - // w.mark("runDescendentsLayout done"); - isRendering = false; - sizeHasChangedDuringRendering = false; - } - - private void layoutSizeMightHaveChanged() { - Size oldSize = new Size(activeLayoutSize.getWidth(), activeLayoutSize - .getHeight()); - calculateLayoutDimensions(); - - /* - * If layout dimension changes we must also update container sizes - */ - if (!oldSize.equals(activeLayoutSize)) { - calculateContainerSize(); - } - } - - private void updateWidgetSizes() { - for (ChildComponentContainer childComponentContainer : widgetToComponentContainer - .values()) { - - /* - * Update widget size from DOM - */ - childComponentContainer.updateWidgetSize(); - } - } - - private void recalculateLayout() { - - /* Calculate space for relative size components */ - int spaceForExpansion = calculateLayoutDimensions(); - - if (!widgetToComponentContainer.isEmpty()) { - /* Divide expansion space between component containers */ - expandComponentContainers(spaceForExpansion); - - /* Update container sizes */ - calculateContainerSize(); - } - - } - - private void expandComponentContainers(int spaceForExpansion) { - int remaining = spaceForExpansion; - for (ChildComponentContainer childComponentContainer : widgetToComponentContainer - .values()) { - remaining -= childComponentContainer.expand(orientation, - spaceForExpansion); - } - - if (remaining > 0) { - // Some left-over pixels due to rounding errors - - // Add one pixel to each container until there are no pixels left - - Iterator widgetIterator = iterator(); - while (widgetIterator.hasNext() && remaining-- > 0) { - ChildComponentContainer childComponentContainer = (ChildComponentContainer) widgetIterator - .next(); - childComponentContainer.expandExtra(orientation, 1); - } - } - - } - - private void handleOrientationUpdate(UIDL uidl) { - int newOrientation = ORIENTATION_VERTICAL; - if ("horizontal".equals(uidl.getStringAttribute("orientation"))) { - newOrientation = ORIENTATION_HORIZONTAL; - } - - if (orientation != newOrientation) { - orientation = newOrientation; - - for (ChildComponentContainer childComponentContainer : widgetToComponentContainer - .values()) { - childComponentContainer.setOrientation(orientation); - } - } - - } - - /** - * Updated components with relative height in horizontal layouts and - * components with relative width in vertical layouts. This is only needed - * if the height (horizontal layout) or width (vertical layout) has not been - * specified. - */ - private boolean updateRelativeSizesInNonMainDirection() { - int updateDirection = 1 - orientation; - if ((updateDirection == ORIENTATION_HORIZONTAL && !isDynamicWidth()) - || (updateDirection == ORIENTATION_VERTICAL && !isDynamicHeight())) { - return false; - } - - boolean updated = false; - for (ChildComponentContainer componentContainer : widgetToComponentContainer - .values()) { - if (componentContainer.isComponentRelativeSized(updateDirection)) { - client.handleComponentRelativeSize(componentContainer - .getWidget()); - } - - updated = true; - } - - return updated; - } - - private int calculateLayoutDimensions() { - int summedWidgetWidth = 0; - int summedWidgetHeight = 0; - - int maxWidgetWidth = 0; - int maxWidgetHeight = 0; - - // Calculate layout dimensions from component dimensions - for (ChildComponentContainer childComponentContainer : widgetToComponentContainer - .values()) { - - int widgetHeight = 0; - int widgetWidth = 0; - if (childComponentContainer.isComponentRelativeSized(orientation)) { - if (orientation == ORIENTATION_HORIZONTAL) { - widgetHeight = getWidgetHeight(childComponentContainer); - } else { - widgetWidth = getWidgetWidth(childComponentContainer); - } - } else { - widgetWidth = getWidgetWidth(childComponentContainer); - widgetHeight = getWidgetHeight(childComponentContainer); - } - - summedWidgetWidth += widgetWidth; - summedWidgetHeight += widgetHeight; - - maxWidgetHeight = Math.max(maxWidgetHeight, widgetHeight); - maxWidgetWidth = Math.max(maxWidgetWidth, widgetWidth); - } - - if (isHorizontal()) { - summedWidgetWidth += activeSpacing.hSpacing - * (widgetToComponentContainer.size() - 1); - } else { - summedWidgetHeight += activeSpacing.vSpacing - * (widgetToComponentContainer.size() - 1); - } - - Size layoutSize = updateLayoutDimensions(summedWidgetWidth, - summedWidgetHeight, maxWidgetWidth, maxWidgetHeight); - - int remainingSpace; - if (isHorizontal()) { - remainingSpace = layoutSize.getWidth() - summedWidgetWidth; - } else { - remainingSpace = layoutSize.getHeight() - summedWidgetHeight; - } - if (remainingSpace < 0) { - remainingSpace = 0; - } - - // ApplicationConnection.getConsole().log( - // "Layout size: " + activeLayoutSize); - return remainingSpace; - } - - private int getWidgetHeight(ChildComponentContainer childComponentContainer) { - Size s = childComponentContainer.getWidgetSize(); - return s.getHeight() - + childComponentContainer.getCaptionHeightAboveComponent(); - } - - private int getWidgetWidth(ChildComponentContainer childComponentContainer) { - Size s = childComponentContainer.getWidgetSize(); - int widgetWidth = s.getWidth() - + childComponentContainer.getCaptionWidthAfterComponent(); - - /* - * If the component does not have a specified size in the main direction - * the caption may determine the space used by the component - */ - if (!childComponentContainer.widgetHasSizeSpecified(orientation)) { - int captionWidth = childComponentContainer - .getCaptionRequiredWidth(); - - if (captionWidth > widgetWidth) { - widgetWidth = captionWidth; - } - } - - return widgetWidth; - } - - private void calculateAlignments() { - int w = 0; - int h = 0; - - if (isHorizontal()) { - // HORIZONTAL - h = activeLayoutSize.getHeight(); - if (!isDynamicWidth()) { - w = -1; - } - - } else { - // VERTICAL - w = activeLayoutSize.getWidth(); - if (!isDynamicHeight()) { - h = -1; - } - } - - for (ChildComponentContainer childComponentContainer : widgetToComponentContainer - .values()) { - childComponentContainer.updateAlignments(w, h); - } - - } - - private void calculateContainerSize() { - - /* - * Container size here means the size the container gets from the - * component. The expansion size is not include in this but taken - * separately into account. - */ - int height = 0, width = 0; - Iterator widgetIterator = iterator(); - if (isHorizontal()) { - height = activeLayoutSize.getHeight(); - int availableWidth = activeLayoutSize.getWidth(); - boolean first = true; - while (widgetIterator.hasNext()) { - ChildComponentContainer childComponentContainer = (ChildComponentContainer) widgetIterator - .next(); - if (!childComponentContainer - .isComponentRelativeSized(ORIENTATION_HORIZONTAL)) { - /* - * Only components with non-relative size in the main - * direction has a container size - */ - width = childComponentContainer.getWidgetSize().getWidth() - + childComponentContainer - .getCaptionWidthAfterComponent(); - - /* - * If the component does not have a specified size in the - * main direction the caption may determine the space used - * by the component - */ - if (!childComponentContainer - .widgetHasSizeSpecified(orientation)) { - int captionWidth = childComponentContainer - .getCaptionRequiredWidth(); - // ApplicationConnection.getConsole().log( - // "Component width: " + width - // + ", caption width: " + captionWidth); - if (captionWidth > width) { - width = captionWidth; - } - } - } else { - width = 0; - } - - if (!isDynamicWidth()) { - if (availableWidth == 0) { - /* - * Let the overflowing components overflow. IE has - * problems with zero sizes. - */ - // width = 0; - // height = 0; - } else if (width > availableWidth) { - width = availableWidth; - - if (!first) { - width -= activeSpacing.hSpacing; - } - availableWidth = 0; - } else { - availableWidth -= width; - if (!first) { - availableWidth -= activeSpacing.hSpacing; - } - } - - first = false; - } - - childComponentContainer.setContainerSize(width, height); - } - } else { - width = activeLayoutSize.getWidth(); - while (widgetIterator.hasNext()) { - ChildComponentContainer childComponentContainer = (ChildComponentContainer) widgetIterator - .next(); - - if (!childComponentContainer - .isComponentRelativeSized(ORIENTATION_VERTICAL)) { - /* - * Only components with non-relative size in the main - * direction has a container size - */ - height = childComponentContainer.getWidgetSize() - .getHeight() - + childComponentContainer - .getCaptionHeightAboveComponent(); - } else { - height = 0; - } - - childComponentContainer.setContainerSize(width, height); - } - - } - - } - - private Size updateLayoutDimensions(int totalComponentWidth, - int totalComponentHeight, int maxComponentWidth, - int maxComponentHeight) { - - /* Only need to calculate dynamic dimensions */ - if (!isDynamicHeight() && !isDynamicWidth()) { - return activeLayoutSize; - } - - int activeLayoutWidth = 0; - int activeLayoutHeight = 0; - - // Update layout dimensions - if (isHorizontal()) { - // Horizontal - if (isDynamicWidth()) { - activeLayoutWidth = totalComponentWidth; - } - - if (isDynamicHeight()) { - activeLayoutHeight = maxComponentHeight; - } - - } else { - // Vertical - if (isDynamicWidth()) { - activeLayoutWidth = maxComponentWidth; - } - - if (isDynamicHeight()) { - activeLayoutHeight = totalComponentHeight; - } - } - - if (isDynamicWidth()) { - setActiveLayoutWidth(activeLayoutWidth); - setOuterLayoutWidth(activeLayoutSize.getWidth()); - } - - if (isDynamicHeight()) { - setActiveLayoutHeight(activeLayoutHeight); - setOuterLayoutHeight(activeLayoutSize.getHeight()); - } - - return activeLayoutSize; - } - - private void setActiveLayoutWidth(int activeLayoutWidth) { - if (activeLayoutWidth < 0) { - activeLayoutWidth = 0; - } - activeLayoutSize.setWidth(activeLayoutWidth); - } - - private void setActiveLayoutHeight(int activeLayoutHeight) { - if (activeLayoutHeight < 0) { - activeLayoutHeight = 0; - } - activeLayoutSize.setHeight(activeLayoutHeight); - - } - - private void setOuterLayoutWidth(int activeLayoutWidth) { - super.setWidth((activeLayoutWidth + activeMargins.getHorizontal()) - + "px"); - - } - - private void setOuterLayoutHeight(int activeLayoutHeight) { - super.setHeight((activeLayoutHeight + activeMargins.getVertical()) - + "px"); - - } - - /** - * Updates the spacing between components. Needs to be done only when - * components are added/removed. - */ - private void updateContainerMargins() { - ChildComponentContainer firstChildComponent = getFirstChildComponentContainer(); - if (firstChildComponent != null) { - firstChildComponent.setMarginLeft(0); - firstChildComponent.setMarginTop(0); - - for (ChildComponentContainer childComponent : widgetToComponentContainer - .values()) { - if (childComponent == firstChildComponent) { - continue; - } - - if (isHorizontal()) { - childComponent.setMarginLeft(activeSpacing.hSpacing); - } else { - childComponent.setMarginTop(activeSpacing.vSpacing); - } - } - } - } - - private boolean isHorizontal() { - return orientation == ORIENTATION_HORIZONTAL; - } - - private boolean isVertical() { - return orientation == ORIENTATION_VERTICAL; - } - - private ChildComponentContainer createChildContainer(Widget child) { - - // Create a container DIV for the child - ChildComponentContainer childComponent = new ChildComponentContainer( - child, orientation); - - return childComponent; - - } - - public RenderSpace getAllocatedSpace(Widget child) { - int width = 0; - int height = 0; - ChildComponentContainer childComponentContainer = getComponentContainer(child); - // WIDTH CALCULATION - if (isVertical()) { - width = activeLayoutSize.getWidth(); - width -= childComponentContainer.getCaptionWidthAfterComponent(); - } else if (!isDynamicWidth()) { - // HORIZONTAL - width = childComponentContainer.getContSize().getWidth(); - width -= childComponentContainer.getCaptionWidthAfterComponent(); - } - - // HEIGHT CALCULATION - if (isHorizontal()) { - height = activeLayoutSize.getHeight(); - height -= childComponentContainer.getCaptionHeightAboveComponent(); - } else if (!isDynamicHeight()) { - // VERTICAL - height = childComponentContainer.getContSize().getHeight(); - height -= childComponentContainer.getCaptionHeightAboveComponent(); - } - - // ApplicationConnection.getConsole().log( - // "allocatedSpace for " + Util.getSimpleName(child) + ": " - // + width + "," + height); - RenderSpace space = new RenderSpace(width, height); - return space; - } - - private void recalculateLayoutAndComponentSizes() { - recalculateLayout(); - - if (!(isDynamicHeight() && isDynamicWidth())) { - /* First update relative sized components */ - for (ChildComponentContainer componentContainer : widgetToComponentContainer - .values()) { - client.handleComponentRelativeSize(componentContainer - .getWidget()); - - // Update widget size from DOM - componentContainer.updateWidgetSize(); - } - } - - if (isDynamicHeight()) { - /* - * Height is not necessarily correct anymore as the height of - * components might have changed if the width has changed. - */ - - /* - * Get the new widget sizes from DOM and calculate new container - * sizes - */ - updateWidgetSizes(); - - /* Update layout dimensions based on widget sizes */ - recalculateLayout(); - } - - updateRelativeSizesInNonMainDirection(); - calculateAlignments(); - - setRootSize(); - } - - private void setRootSize() { - root.getStyle().setPropertyPx("width", activeLayoutSize.getWidth()); - root.getStyle().setPropertyPx("height", activeLayoutSize.getHeight()); - } - - public boolean requestLayout(Set children) { - for (Paintable p : children) { - /* Update widget size from DOM */ - ChildComponentContainer componentContainer = getComponentContainer((Widget) p); - // This should no longer be needed (after #2563) - // if (isDynamicWidth()) { - // componentContainer.setUnlimitedContainerWidth(); - // } else { - // componentContainer.setLimitedContainerWidth(activeLayoutSize - // .getWidth()); - // } - - componentContainer.updateWidgetSize(); - - /* - * If this is the result of an caption icon onload event the caption - * size may have changed - */ - componentContainer.updateCaptionSize(); - } - - Size sizeBefore = new Size(activeLayoutSize.getWidth(), - activeLayoutSize.getHeight()); - - recalculateLayoutAndComponentSizes(); - boolean sameSize = (sizeBefore.equals(activeLayoutSize)); - if (!sameSize) { - /* Must inform child components about possible size updates */ - client.runDescendentsLayout(this); - } - - /* Automatically propagated upwards if the size has changed */ - - return sameSize; - } - - @Override - public void setHeight(String height) { - Size sizeBefore = new Size(activeLayoutSize.getWidth(), - activeLayoutSize.getHeight()); - - super.setHeight(height); - - if (height != null && !height.equals("")) { - setActiveLayoutHeight(getOffsetHeight() - - activeMargins.getVertical()); - } - - if (isRendering) { - sizeHasChangedDuringRendering = true; - } else { - recalculateLayoutAndComponentSizes(); - boolean sameSize = (sizeBefore.equals(activeLayoutSize)); - if (!sameSize) { - /* Must inform child components about possible size updates */ - client.runDescendentsLayout(this); - } - } - } - - @Override - public void setWidth(String width) { - if (this.width.equals(width)) { - return; - } - Size sizeBefore = new Size(activeLayoutSize.getWidth(), - activeLayoutSize.getHeight()); - - super.setWidth(width); - this.width = width; - if (width != null && !width.equals("")) { - setActiveLayoutWidth(getOffsetWidth() - - activeMargins.getHorizontal()); - } - - if (isRendering) { - sizeHasChangedDuringRendering = true; - } else { - recalculateLayoutAndComponentSizes(); - boolean sameSize = (sizeBefore.equals(activeLayoutSize)); - if (!sameSize) { - /* Must inform child components about possible size updates */ - client.runDescendentsLayout(this); - } - /* - * If the height changes as a consequence of this we must inform the - * parent also - */ - if (isDynamicHeight() - && sizeBefore.getHeight() != activeLayoutSize.getHeight()) { - Util.notifyParentOfSizeChange(this, false); - } - - } - } - - protected void updateAlignmentsAndExpandRatios(UIDL uidl, - ArrayList renderedWidgets) { - - /* - * UIDL contains component alignments as a comma separated list. - * - * See com.vaadin.terminal.gwt.client.ui.AlignmentInfo.java for - * possible values. - */ - final int[] alignments = uidl.getIntArrayAttribute("alignments"); - - /* - * UIDL contains normalized expand ratios as a comma separated list. - */ - final int[] expandRatios = uidl.getIntArrayAttribute("expandRatios"); - - for (int i = 0; i < renderedWidgets.size(); i++) { - Widget widget = renderedWidgets.get(i); - - ChildComponentContainer container = getComponentContainer(widget); - - // Calculate alignment info - container.setAlignment(new AlignmentInfo(alignments[i])); - - // Update expand ratio - container.setExpandRatio(expandRatios[i]); - } - } - - public void updateCaption(Paintable component, UIDL uidl) { - ChildComponentContainer componentContainer = getComponentContainer((Widget) component); - componentContainer.updateCaption(uidl, client); - if (!isRendering) { - /* - * This was a component-only update and the possible size change - * must be propagated to the layout - */ - client.captionSizeUpdated(component); - } - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IPanel.java b/src/com/vaadin/terminal/gwt/client/ui/IPanel.java deleted file mode 100644 index 7be9a112b3..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IPanel.java +++ /dev/null @@ -1,516 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.Set; - -import com.google.gwt.dom.client.DivElement; -import com.google.gwt.dom.client.Document; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.SimplePanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.Container; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.RenderInformation; -import com.vaadin.terminal.gwt.client.RenderSpace; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; - -public class IPanel extends SimplePanel implements Container { - - public static final String CLASSNAME = "i-panel"; - - ApplicationConnection client; - - String id; - - private final Element captionNode = DOM.createDiv(); - - private final Element captionText = DOM.createSpan(); - - private Icon icon; - - private final Element bottomDecoration = DOM.createDiv(); - - private final Element contentNode = DOM.createDiv(); - - private Element errorIndicatorElement; - - private String height; - - private Paintable layout; - - ShortcutActionHandler shortcutHandler; - - private String width = ""; - - private Element geckoCaptionMeter; - - private int scrollTop; - - private int scrollLeft; - - private RenderInformation renderInformation = new RenderInformation(); - - private int borderPaddingHorizontal = -1; - - private int borderPaddingVertical = -1; - - private int captionPaddingHorizontal = -1; - - private int captionMarginLeft = -1; - - private boolean rendering; - - private int contentMarginLeft = -1; - - private String previousStyleName; - - public IPanel() { - super(); - DivElement captionWrap = Document.get().createDivElement(); - captionWrap.appendChild(captionNode); - captionNode.appendChild(captionText); - - captionWrap.setClassName(CLASSNAME + "-captionwrap"); - captionNode.setClassName(CLASSNAME + "-caption"); - contentNode.setClassName(CLASSNAME + "-content"); - bottomDecoration.setClassName(CLASSNAME + "-deco"); - - getElement().appendChild(captionWrap); - getElement().appendChild(contentNode); - getElement().appendChild(bottomDecoration); - setStyleName(CLASSNAME); - DOM.sinkEvents(getElement(), Event.ONKEYDOWN); - DOM.sinkEvents(contentNode, Event.ONSCROLL); - contentNode.getStyle().setProperty("position", "relative"); - getElement().getStyle().setProperty("overflow", "hidden"); - } - - @Override - protected Element getContainerElement() { - return contentNode; - } - - private void setCaption(String text) { - DOM.setInnerHTML(captionText, text); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - rendering = true; - if (!uidl.hasAttribute("cached")) { - // Handle caption displaying and style names, prior generics. - // Affects size - // calculations - - // Restore default stylenames - contentNode.setClassName(CLASSNAME + "-content"); - bottomDecoration.setClassName(CLASSNAME + "-deco"); - captionNode.setClassName(CLASSNAME + "-caption"); - boolean hasCaption = false; - if (uidl.hasAttribute("caption") - && !uidl.getStringAttribute("caption").equals("")) { - setCaption(uidl.getStringAttribute("caption")); - hasCaption = true; - } else { - setCaption(""); - captionNode.setClassName(CLASSNAME + "-nocaption"); - } - - // Add proper stylenames for all elements. This way we can prevent - // unwanted CSS selector inheritance. - if (uidl.hasAttribute("style")) { - final String[] styles = uidl.getStringAttribute("style").split( - " "); - final String captionBaseClass = CLASSNAME - + (hasCaption ? "-caption" : "-nocaption"); - final String contentBaseClass = CLASSNAME + "-content"; - final String decoBaseClass = CLASSNAME + "-deco"; - String captionClass = captionBaseClass; - String contentClass = contentBaseClass; - String decoClass = decoBaseClass; - for (int i = 0; i < styles.length; i++) { - captionClass += " " + captionBaseClass + "-" + styles[i]; - contentClass += " " + contentBaseClass + "-" + styles[i]; - decoClass += " " + decoBaseClass + "-" + styles[i]; - } - captionNode.setClassName(captionClass); - contentNode.setClassName(contentClass); - bottomDecoration.setClassName(decoClass); - - } - } - // Ensure correct implementation - if (client.updateComponent(this, uidl, false)) { - rendering = false; - return; - } - - this.client = client; - id = uidl.getId(); - - setIconUri(uidl, client); - - handleError(uidl); - - // Render content - final UIDL layoutUidl = uidl.getChildUIDL(0); - final Paintable newLayout = client.getPaintable(layoutUidl); - if (newLayout != layout) { - if (layout != null) { - client.unregisterPaintable(layout); - } - setWidget((Widget) newLayout); - layout = newLayout; - } - layout.updateFromUIDL(layoutUidl, client); - - runHacks(false); - // We may have actions attached to this panel - if (uidl.getChildCount() > 1) { - final int cnt = uidl.getChildCount(); - for (int i = 1; i < cnt; i++) { - UIDL childUidl = uidl.getChildUIDL(i); - if (childUidl.getTag().equals("actions")) { - if (shortcutHandler == null) { - shortcutHandler = new ShortcutActionHandler(id, client); - } - shortcutHandler.updateActionMap(childUidl); - } - } - } - - if (uidl.hasVariable("scrollTop") - && uidl.getIntVariable("scrollTop") != scrollTop) { - scrollTop = uidl.getIntVariable("scrollTop"); - DOM.setElementPropertyInt(contentNode, "scrollTop", scrollTop); - } - - if (uidl.hasVariable("scrollLeft") - && uidl.getIntVariable("scrollLeft") != scrollLeft) { - scrollLeft = uidl.getIntVariable("scrollLeft"); - DOM.setElementPropertyInt(contentNode, "scrollLeft", scrollLeft); - } - - rendering = false; - - } - - @Override - public void setStyleName(String style) { - if (!style.equals(previousStyleName)) { - super.setStyleName(style); - detectContainerBorders(); - previousStyleName = style; - } - } - - private void handleError(UIDL uidl) { - if (uidl.hasAttribute("error")) { - if (errorIndicatorElement == null) { - errorIndicatorElement = DOM.createDiv(); - DOM.setElementProperty(errorIndicatorElement, "className", - "i-errorindicator"); - DOM.sinkEvents(errorIndicatorElement, Event.MOUSEEVENTS); - sinkEvents(Event.MOUSEEVENTS); - } - DOM.insertBefore(captionNode, errorIndicatorElement, captionText); - } else if (errorIndicatorElement != null) { - DOM.removeChild(captionNode, errorIndicatorElement); - errorIndicatorElement = null; - } - } - - private void setIconUri(UIDL uidl, ApplicationConnection client) { - final String iconUri = uidl.hasAttribute("icon") ? uidl - .getStringAttribute("icon") : null; - if (iconUri == null) { - if (icon != null) { - DOM.removeChild(captionNode, icon.getElement()); - icon = null; - } - } else { - if (icon == null) { - icon = new Icon(client); - DOM.insertChild(captionNode, icon.getElement(), 0); - } - icon.setUri(iconUri); - } - } - - public void runHacks(boolean runGeckoFix) { - if (BrowserInfo.get().isIE6() && width != null && !width.equals("")) { - /* - * IE6 requires overflow-hidden elements to have a width specified - * so we calculate the width of the content and caption nodes when - * no width has been specified. - */ - /* - * Fixes #1923 IPanel: Horizontal scrollbar does not appear in IE6 - * with wide content - */ - - /* - * Caption must be shrunk for parent measurements to return correct - * result in IE6 - */ - DOM.setStyleAttribute(captionNode, "width", "1px"); - - int parentPadding = Util.measureHorizontalPaddingAndBorder( - getElement(), 0); - - int parentWidthExcludingPadding = getElement().getOffsetWidth() - - parentPadding; - - Util.setWidthExcludingPaddingAndBorder(captionNode, - parentWidthExcludingPadding - getCaptionMarginLeft(), 26, - false); - - int contentMarginLeft = getContentMarginLeft(); - - Util.setWidthExcludingPaddingAndBorder(contentNode, - parentWidthExcludingPadding - contentMarginLeft, 2, false); - - } - - if ((BrowserInfo.get().isIE() || BrowserInfo.get().isFF2()) - && (width == null || width.equals(""))) { - /* - * IE and FF2 needs width to be specified for the root DIV so we - * calculate that from the sizes of the caption and layout - */ - int captionWidth = captionText.getOffsetWidth() - + getCaptionMarginLeft() + getCaptionPaddingHorizontal(); - int layoutWidth = ((Widget) layout).getOffsetWidth() - + getContainerBorderWidth(); - int width = layoutWidth; - if (captionWidth > width) { - width = captionWidth; - } - - if (BrowserInfo.get().isIE7()) { - Util.setWidthExcludingPaddingAndBorder(captionNode, width - - getCaptionMarginLeft(), 26, false); - } - - super.setWidth(width + "px"); - } - - if (runGeckoFix && BrowserInfo.get().isGecko()) { - // workaround for #1764 - if (width == null || width.equals("")) { - if (geckoCaptionMeter == null) { - geckoCaptionMeter = DOM.createDiv(); - DOM.appendChild(captionNode, geckoCaptionMeter); - } - int captionWidth = DOM.getElementPropertyInt(captionText, - "offsetWidth"); - int availWidth = DOM.getElementPropertyInt(geckoCaptionMeter, - "offsetWidth"); - if (captionWidth == availWidth) { - /* - * Caption width defines panel width -> Gecko based browsers - * somehow fails to float things right, without the - * "noncode" below - */ - setWidth(getOffsetWidth() + "px"); - } else { - DOM.setStyleAttribute(captionNode, "width", ""); - } - } - } - - client.runDescendentsLayout(this); - - Util.runWebkitOverflowAutoFix(contentNode); - - } - - @Override - public void onBrowserEvent(Event event) { - final Element target = DOM.eventGetTarget(event); - final int type = DOM.eventGetType(event); - if (type == Event.ONKEYDOWN && shortcutHandler != null) { - shortcutHandler.handleKeyboardEvent(event); - return; - } - if (type == Event.ONSCROLL) { - int newscrollTop = DOM.getElementPropertyInt(contentNode, - "scrollTop"); - int newscrollLeft = DOM.getElementPropertyInt(contentNode, - "scrollLeft"); - if (client != null - && (newscrollLeft != scrollLeft || newscrollTop != scrollTop)) { - scrollLeft = newscrollLeft; - scrollTop = newscrollTop; - client.updateVariable(id, "scrollTop", scrollTop, false); - client.updateVariable(id, "scrollLeft", scrollLeft, false); - } - } else if (captionNode.isOrHasChild(target)) { - if (client != null) { - client.handleTooltipEvent(event, this); - } - } - } - - @Override - public void setHeight(String height) { - this.height = height; - super.setHeight(height); - if (height != null && height != "") { - final int targetHeight = getOffsetHeight(); - int containerHeight = targetHeight - captionNode.getOffsetHeight() - - bottomDecoration.getOffsetHeight() - - getContainerBorderHeight(); - if (containerHeight < 0) { - containerHeight = 0; - } - DOM - .setStyleAttribute(contentNode, "height", containerHeight - + "px"); - } else { - DOM.setStyleAttribute(contentNode, "height", ""); - } - if (!rendering) { - runHacks(true); - } - } - - private int getCaptionMarginLeft() { - if (captionMarginLeft < 0) { - detectContainerBorders(); - } - return captionMarginLeft; - } - - private int getContentMarginLeft() { - if (contentMarginLeft < 0) { - detectContainerBorders(); - } - return contentMarginLeft; - } - - private int getCaptionPaddingHorizontal() { - if (captionPaddingHorizontal < 0) { - detectContainerBorders(); - } - return captionPaddingHorizontal; - } - - private int getContainerBorderHeight() { - if (borderPaddingVertical < 0) { - detectContainerBorders(); - } - return borderPaddingVertical; - } - - @Override - public void setWidth(String width) { - if (this.width.equals(width)) { - return; - } - - this.width = width; - super.setWidth(width); - if (!rendering) { - runHacks(true); - - if (height.equals("")) { - // Width change may affect height - Util.updateRelativeChildrenAndSendSizeUpdateEvent(client, this); - } - - } - } - - private int getContainerBorderWidth() { - if (borderPaddingHorizontal < 0) { - detectContainerBorders(); - } - return borderPaddingHorizontal; - } - - private void detectContainerBorders() { - DOM.setStyleAttribute(contentNode, "overflow", "hidden"); - - borderPaddingHorizontal = Util.measureHorizontalBorder(contentNode); - borderPaddingVertical = Util.measureVerticalBorder(contentNode); - - DOM.setStyleAttribute(contentNode, "overflow", "auto"); - - captionPaddingHorizontal = Util.measureHorizontalPaddingAndBorder( - captionNode, 26); - - captionMarginLeft = Util.measureMarginLeft(captionNode); - contentMarginLeft = Util.measureMarginLeft(contentNode); - - } - - public boolean hasChildComponent(Widget component) { - if (component != null && component == layout) { - return true; - } else { - return false; - } - } - - public void replaceChildComponent(Widget oldComponent, Widget newComponent) { - // TODO This is untested as no layouts require this - if (oldComponent != layout) { - return; - } - - setWidget(newComponent); - layout = (Paintable) newComponent; - } - - public RenderSpace getAllocatedSpace(Widget child) { - int w = 0; - int h = 0; - - if (width != null && !width.equals("")) { - w = getOffsetWidth() - getContainerBorderWidth(); - if (w < 0) { - w = 0; - } - } - - if (height != null && !height.equals("")) { - h = contentNode.getOffsetHeight() - getContainerBorderHeight(); - if (h < 0) { - h = 0; - } - } - - return new RenderSpace(w, h, true); - } - - public boolean requestLayout(Set child) { - if (height != null && height != "" && width != null && width != "") { - /* - * If the height and width has been specified the child components - * cannot make the size of the layout change - */ - return true; - } - runHacks(false); - return !renderInformation.updateSize(getElement()); - } - - public void updateCaption(Paintable component, UIDL uidl) { - // NOP: layouts caption, errors etc not rendered in Panel - } - - @Override - protected void onAttach() { - super.onAttach(); - detectContainerBorders(); - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IPasswordField.java b/src/com/vaadin/terminal/gwt/client/ui/IPasswordField.java deleted file mode 100644 index 569076e353..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IPasswordField.java +++ /dev/null @@ -1,21 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import com.google.gwt.user.client.DOM; - -/** - * This class represents a password field. - * - * @author IT Mill Ltd. - * - */ -public class IPasswordField extends ITextField { - - public IPasswordField() { - super(DOM.createInputPassword()); - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IPopupCalendar.java b/src/com/vaadin/terminal/gwt/client/ui/IPopupCalendar.java deleted file mode 100644 index 0b876f1cb2..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IPopupCalendar.java +++ /dev/null @@ -1,130 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.ui.Button; -import com.google.gwt.user.client.ui.ClickListener; -import com.google.gwt.user.client.ui.PopupListener; -import com.google.gwt.user.client.ui.PopupPanel; -import com.google.gwt.user.client.ui.Widget; -import com.google.gwt.user.client.ui.PopupPanel.PositionCallback; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; - -public class IPopupCalendar extends ITextualDate implements Paintable, Field, - ClickListener, PopupListener { - - private final Button calendarToggle; - - private final ICalendarPanel calendar; - - private final IToolkitOverlay popup; - private boolean open = false; - - public IPopupCalendar() { - super(); - - calendarToggle = new Button(); - calendarToggle.setStyleName(CLASSNAME + "-button"); - calendarToggle.setText(""); - calendarToggle.addClickListener(this); - add(calendarToggle); - - calendar = new ICalendarPanel(this); - popup = new IToolkitOverlay(true, true, true); - popup.setStyleName(IDateField.CLASSNAME + "-popup"); - popup.setWidget(calendar); - popup.addPopupListener(this); - - DOM.setElementProperty(calendar.getElement(), "id", - "PID_TOOLKIT_POPUPCAL"); - - } - - @Override - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - super.updateFromUIDL(uidl, client); - if (date != null) { - calendar.updateCalendar(); - } - calendarToggle.setEnabled(enabled); - } - - public void onClick(Widget sender) { - if (sender == calendarToggle && !open) { - open = true; - calendar.updateCalendar(); - // clear previous values - popup.setWidth(""); - popup.setHeight(""); - popup.setPopupPositionAndShow(new PositionCallback() { - public void setPosition(int offsetWidth, int offsetHeight) { - final int w = offsetWidth; - final int h = offsetHeight; - int t = calendarToggle.getAbsoluteTop(); - int l = calendarToggle.getAbsoluteLeft(); - if (l + w > Window.getClientWidth() - + Window.getScrollLeft()) { - l = Window.getClientWidth() + Window.getScrollLeft() - - w; - } - if (t + h + calendarToggle.getOffsetHeight() + 30 > Window - .getClientHeight() - + Window.getScrollTop()) { - t = Window.getClientHeight() + Window.getScrollTop() - - h - calendarToggle.getOffsetHeight() - 30; - l += calendarToggle.getOffsetWidth(); - } - - // fix size - popup.setWidth(w + "px"); - popup.setHeight(h + "px"); - - popup.setPopupPosition(l, t - + calendarToggle.getOffsetHeight() + 2); - - setFocus(true); - } - }); - } - } - - public void onPopupClosed(PopupPanel sender, boolean autoClosed) { - if (sender == popup) { - buildDate(); - // Sigh. - Timer t = new Timer() { - @Override - public void run() { - open = false; - } - }; - t.schedule(100); - } - } - - /** - * Sets focus to Calendar panel. - * - * @param focus - */ - public void setFocus(boolean focus) { - calendar.setFocus(focus); - } - - @Override - protected int getFieldExtraWidth() { - if (fieldExtraWidth < 0) { - fieldExtraWidth = super.getFieldExtraWidth(); - fieldExtraWidth += calendarToggle.getOffsetWidth(); - } - return fieldExtraWidth; - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IPopupView.java b/src/com/vaadin/terminal/gwt/client/ui/IPopupView.java deleted file mode 100644 index 8e89b451c4..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IPopupView.java +++ /dev/null @@ -1,417 +0,0 @@ -package com.vaadin.terminal.gwt.client.ui; - -import java.util.HashSet; -import java.util.Set; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.ClickListener; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.HasFocus; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.PopupListener; -import com.google.gwt.user.client.ui.PopupPanel; -import com.google.gwt.user.client.ui.RootPanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.Container; -import com.vaadin.terminal.gwt.client.ICaption; -import com.vaadin.terminal.gwt.client.ICaptionWrapper; -import com.vaadin.terminal.gwt.client.ITooltip; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.RenderSpace; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; -import com.vaadin.terminal.gwt.client.RenderInformation.Size; - -public class IPopupView extends HTML implements Container { - - public static final String CLASSNAME = "i-popupview"; - - /** For server-client communication */ - private String uidlId; - private ApplicationConnection client; - - /** This variable helps to communicate popup visibility to the server */ - private boolean hostPopupVisible; - - private final CustomPopup popup; - private final Label loading = new Label("Loading..."); - - /** - * loading constructor - */ - public IPopupView() { - super(); - popup = new CustomPopup(); - - setStyleName(CLASSNAME); - popup.setStylePrimaryName(CLASSNAME + "-popup"); - - setHTML("(No HTML defined for PopupView)"); - popup.setWidget(loading); - - // When we click to open the popup... - addClickListener(new ClickListener() { - public void onClick(Widget sender) { - updateState(true); - } - }); - - // ..and when we close it - popup.addPopupListener(new PopupListener() { - public void onPopupClosed(PopupPanel sender, boolean autoClosed) { - updateState(false); - } - }); - - popup.setAnimationEnabled(true); - sinkEvents(ITooltip.TOOLTIP_EVENTS); - } - - /** - * - * - * @see com.vaadin.terminal.gwt.client.Paintable#updateFromUIDL(com.vaadin.terminal.gwt.client.UIDL, - * com.vaadin.terminal.gwt.client.ApplicationConnection) - */ - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - // This call should be made first. Ensure correct implementation, - // and don't let the containing layout manage caption. - if (client.updateComponent(this, uidl, false)) { - return; - } - // These are for future server connections - this.client = client; - uidlId = uidl.getId(); - - hostPopupVisible = uidl.getBooleanVariable("popupVisibility"); - - setHTML(uidl.getStringAttribute("html")); - - if (uidl.hasAttribute("hideOnMouseOut")) { - popup.setHideOnMouseOut(uidl.getBooleanAttribute("hideOnMouseOut")); - } - - // Render the popup if visible and show it. - if (hostPopupVisible) { - UIDL popupUIDL = uidl.getChildUIDL(0); - - // showPopupOnTop(popup, hostReference); - preparePopup(popup); - popup.updateFromUIDL(popupUIDL, client); - if (uidl.hasAttribute("style")) { - final String[] styles = uidl.getStringAttribute("style").split( - " "); - final StringBuffer styleBuf = new StringBuffer(); - final String primaryName = popup.getStylePrimaryName(); - styleBuf.append(primaryName); - for (int i = 0; i < styles.length; i++) { - styleBuf.append(" "); - styleBuf.append(primaryName); - styleBuf.append("-"); - styleBuf.append(styles[i]); - } - popup.setStyleName(styleBuf.toString()); - } else { - popup.setStyleName(popup.getStylePrimaryName()); - } - showPopup(popup); - - // The popup shouldn't be visible, try to hide it. - } else { - popup.hide(); - } - }// updateFromUIDL - - /** - * Update popup visibility to server - * - * @param visibility - */ - private void updateState(boolean visible) { - // If we know the server connection - // then update the current situation - if (uidlId != null && client != null && isAttached()) { - client.updateVariable(uidlId, "popupVisibility", visible, true); - } - } - - private void preparePopup(final CustomPopup popup) { - popup.setVisible(false); - popup.show(); - } - - private void showPopup(final CustomPopup popup) { - int windowTop = RootPanel.get().getAbsoluteTop(); - int windowLeft = RootPanel.get().getAbsoluteLeft(); - int windowRight = windowLeft + RootPanel.get().getOffsetWidth(); - int windowBottom = windowTop + RootPanel.get().getOffsetHeight(); - - int offsetWidth = popup.getOffsetWidth(); - int offsetHeight = popup.getOffsetHeight(); - - int hostHorizontalCenter = IPopupView.this.getAbsoluteLeft() - + IPopupView.this.getOffsetWidth() / 2; - int hostVerticalCenter = IPopupView.this.getAbsoluteTop() - + IPopupView.this.getOffsetHeight() / 2; - - int left = hostHorizontalCenter - offsetWidth / 2; - int top = hostVerticalCenter - offsetHeight / 2; - - // Superclass takes care of top and left - if ((left + offsetWidth) > windowRight) { - left -= (left + offsetWidth) - windowRight; - } - - if ((top + offsetHeight) > windowBottom) { - top -= (top + offsetHeight) - windowBottom; - } - - popup.setPopupPosition(left, top); - - popup.setVisible(true); - } - - /** - * Make sure that we remove the popup when the main widget is removed. - * - * @see com.google.gwt.user.client.ui.Widget#onUnload() - */ - @Override - protected void onDetach() { - popup.hide(); - super.onDetach(); - } - - private static native void nativeBlur(Element e) - /*-{ - if(e && e.blur) { - e.blur(); - } - }-*/; - - private class CustomPopup extends IToolkitOverlay { - - private Paintable popupComponentPaintable = null; - private Widget popupComponentWidget = null; - private ICaptionWrapper captionWrapper = null; - - private boolean hasHadMouseOver = false; - private boolean hideOnMouseOut = true; - private final Set activeChildren = new HashSet(); - private boolean hiding = false; - - public CustomPopup() { - super(true, false, true); // autoHide, not modal, dropshadow - } - - // For some reason ONMOUSEOUT events are not always received, so we have - // to use ONMOUSEMOVE that doesn't target the popup - @Override - public boolean onEventPreview(Event event) { - Element target = DOM.eventGetTarget(event); - boolean eventTargetsPopup = DOM.isOrHasChild(getElement(), target); - int type = DOM.eventGetType(event); - - // Catch children that use keyboard, so we can unfocus them when - // hiding - if (eventTargetsPopup && type == Event.ONKEYPRESS) { - activeChildren.add(target); - } - - if (eventTargetsPopup && type == Event.ONMOUSEMOVE) { - hasHadMouseOver = true; - } - - if (!eventTargetsPopup && type == Event.ONMOUSEMOVE) { - - if (hasHadMouseOver && hideOnMouseOut) { - hide(); - return true; - } - } - - return super.onEventPreview(event); - } - - @Override - public void hide(boolean autoClosed) { - hiding = true; - syncChildren(); - unregisterPaintables(); - if (popupComponentWidget != null && popupComponentWidget != loading) { - remove(popupComponentWidget); - } - hasHadMouseOver = false; - super.hide(autoClosed); - } - - @Override - public void show() { - hiding = false; - super.show(); - } - - /** - * Try to sync all known active child widgets to server - */ - public void syncChildren() { - // Notify children with focus - if ((popupComponentWidget instanceof HasFocus)) { - ((HasFocus) popupComponentWidget).setFocus(false); - } - - // Notify children that have used the keyboard - for (Element e : activeChildren) { - try { - nativeBlur(e); - } catch (Exception ignored) { - } - } - activeChildren.clear(); - } - - @Override - public boolean remove(Widget w) { - - popupComponentPaintable = null; - popupComponentWidget = null; - captionWrapper = null; - - return super.remove(w); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - - Paintable newPopupComponent = client.getPaintable(uidl - .getChildUIDL(0)); - - if (newPopupComponent != popupComponentPaintable) { - - setWidget((Widget) newPopupComponent); - - popupComponentWidget = (Widget) newPopupComponent; - - popupComponentPaintable = newPopupComponent; - } - - popupComponentPaintable - .updateFromUIDL(uidl.getChildUIDL(0), client); - - } - - public void unregisterPaintables() { - if (popupComponentPaintable != null) { - client.unregisterPaintable(popupComponentPaintable); - } - } - - public void setHideOnMouseOut(boolean hideOnMouseOut) { - this.hideOnMouseOut = hideOnMouseOut; - } - - /* - * - * We need a hack make popup act as a child of IPopupView in toolkits - * component tree, but work in default GWT manner when closing or - * opening. - * - * (non-Javadoc) - * - * @see com.google.gwt.user.client.ui.Widget#getParent() - */ - @Override - public Widget getParent() { - if (!isAttached() || hiding) { - return super.getParent(); - } else { - return IPopupView.this; - } - } - - @Override - protected void onDetach() { - super.onDetach(); - hiding = false; - } - - @Override - public Element getContainerElement() { - return super.getContainerElement(); - } - - }// class CustomPopup - - // Container methods - - public RenderSpace getAllocatedSpace(Widget child) { - Size popupExtra = calculatePopupExtra(); - - return new RenderSpace(RootPanel.get().getOffsetWidth() - - popupExtra.getWidth(), RootPanel.get().getOffsetHeight() - - popupExtra.getHeight()); - } - - /** - * Calculate extra space taken by the popup decorations - * - * @return - */ - protected Size calculatePopupExtra() { - Element pe = popup.getElement(); - Element ipe = popup.getContainerElement(); - - // border + padding - int width = Util.getRequiredWidth(pe) - Util.getRequiredWidth(ipe); - int height = Util.getRequiredHeight(pe) - Util.getRequiredHeight(ipe); - - return new Size(width, height); - } - - public boolean hasChildComponent(Widget component) { - if (popup.popupComponentWidget != null) { - return popup.popupComponentWidget == component; - } else { - return false; - } - } - - public void replaceChildComponent(Widget oldComponent, Widget newComponent) { - popup.setWidget(newComponent); - popup.popupComponentWidget = newComponent; - } - - public boolean requestLayout(Set child) { - return true; - } - - public void updateCaption(Paintable component, UIDL uidl) { - if (ICaption.isNeeded(uidl)) { - if (popup.captionWrapper != null) { - popup.captionWrapper.updateCaption(uidl); - } else { - popup.captionWrapper = new ICaptionWrapper(component, client); - popup.setWidget(popup.captionWrapper); - popup.captionWrapper.updateCaption(uidl); - } - } else { - if (popup.captionWrapper != null) { - popup.setWidget(popup.popupComponentWidget); - } - } - - popup.popupComponentWidget = (Widget) component; - popup.popupComponentPaintable = component; - } - - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - if (client != null) { - client.handleTooltipEvent(event, this); - } - } - -}// class IPopupView diff --git a/src/com/vaadin/terminal/gwt/client/ui/IProgressIndicator.java b/src/com/vaadin/terminal/gwt/client/ui/IProgressIndicator.java deleted file mode 100644 index 0490dd048f..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IProgressIndicator.java +++ /dev/null @@ -1,100 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; - -public class IProgressIndicator extends Widget implements Paintable { - - private static final String CLASSNAME = "i-progressindicator"; - Element wrapper = DOM.createDiv(); - Element indicator = DOM.createDiv(); - private ApplicationConnection client; - private final Poller poller; - private boolean indeterminate = false; - private boolean pollerSuspendedDueDetach; - - public IProgressIndicator() { - setElement(DOM.createDiv()); - getElement().appendChild(wrapper); - setStyleName(CLASSNAME); - wrapper.appendChild(indicator); - indicator.setClassName(CLASSNAME + "-indicator"); - wrapper.setClassName(CLASSNAME + "-wrapper"); - poller = new Poller(); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - if (client.updateComponent(this, uidl, true)) { - return; - } - - poller.cancel(); - this.client = client; - if (client.updateComponent(this, uidl, true)) { - return; - } - - indeterminate = uidl.getBooleanAttribute("indeterminate"); - - if (indeterminate) { - String basename = CLASSNAME + "-indeterminate"; - IProgressIndicator.setStyleName(getElement(), basename, true); - IProgressIndicator.setStyleName(getElement(), basename - + "-disabled", uidl.getBooleanAttribute("disabled")); - } else { - try { - final float f = Float.parseFloat(uidl - .getStringAttribute("state")); - final int size = Math.round(100 * f); - DOM.setStyleAttribute(indicator, "width", size + "%"); - } catch (final Exception e) { - } - } - - if (!uidl.getBooleanAttribute("disabled")) { - poller.scheduleRepeating(uidl.getIntAttribute("pollinginterval")); - } - } - - @Override - protected void onAttach() { - super.onAttach(); - if (pollerSuspendedDueDetach) { - poller.run(); - } - } - - @Override - protected void onDetach() { - super.onDetach(); - poller.cancel(); - pollerSuspendedDueDetach = true; - } - - @Override - public void setVisible(boolean visible) { - super.setVisible(visible); - if (!visible) { - poller.cancel(); - } - } - - class Poller extends Timer { - - @Override - public void run() { - client.sendPendingVariableChanges(); - } - - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IScrollTable.java b/src/com/vaadin/terminal/gwt/client/ui/IScrollTable.java deleted file mode 100644 index 2b085ebd87..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IScrollTable.java +++ /dev/null @@ -1,2841 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.Vector; - -import com.google.gwt.dom.client.Document; -import com.google.gwt.dom.client.NodeList; -import com.google.gwt.dom.client.TableCellElement; -import com.google.gwt.dom.client.TableRowElement; -import com.google.gwt.dom.client.TableSectionElement; -import com.google.gwt.user.client.Command; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.DeferredCommand; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.Panel; -import com.google.gwt.user.client.ui.RootPanel; -import com.google.gwt.user.client.ui.ScrollListener; -import com.google.gwt.user.client.ui.ScrollPanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.Container; -import com.vaadin.terminal.gwt.client.MouseEventDetails; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.RenderSpace; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; -import com.vaadin.terminal.gwt.client.ui.IScrollTable.IScrollTableBody.IScrollTableRow; - -/** - * IScrollTable - * - * IScrollTable is a FlowPanel having two widgets in it: * TableHead component * - * ScrollPanel - * - * TableHead contains table's header and widgets + logic for resizing, - * reordering and hiding columns. - * - * ScrollPanel contains IScrollTableBody object which handles content. To save - * some bandwidth and to improve clients responsiveness with loads of data, in - * IScrollTableBody all rows are not necessary rendered. There are "spacers" in - * IScrollTableBody to use the exact same space as non-rendered rows would use. - * This way we can use seamlessly traditional scrollbars and scrolling to fetch - * more rows instead of "paging". - * - * In IScrollTable we listen to scroll events. On horizontal scrolling we also - * update TableHeads scroll position which has its scrollbars hidden. On - * vertical scroll events we will check if we are reaching the end of area where - * we have rows rendered and - * - * TODO implement unregistering for child components in Cells - */ -public class IScrollTable extends FlowPanel implements Table, ScrollListener { - - public static final String CLASSNAME = "i-table"; - /** - * multiple of pagelength which component will cache when requesting more - * rows - */ - private static final double CACHE_RATE = 2; - /** - * fraction of pageLenght which can be scrolled without making new request - */ - private static final double CACHE_REACT_RATE = 1.5; - - public static final char ALIGN_CENTER = 'c'; - public static final char ALIGN_LEFT = 'b'; - public static final char ALIGN_RIGHT = 'e'; - private int firstRowInViewPort = 0; - private int pageLength = 15; - private int lastRequestedFirstvisible = 0; // to detect "serverside scroll" - - private boolean showRowHeaders = false; - - private String[] columnOrder; - - private ApplicationConnection client; - private String paintableId; - - private boolean immediate; - - private int selectMode = Table.SELECT_MODE_NONE; - - private final HashSet selectedRowKeys = new HashSet(); - - private boolean initializedAndAttached = false; - - /** - * Flag to indicate if a column width recalculation is needed due update. - */ - private boolean headerChangedDuringUpdate = false; - - private final TableHead tHead = new TableHead(); - - private final ScrollPanel bodyContainer = new ScrollPanel(); - - private int totalRows; - - private Set collapsedColumns; - - private final RowRequestHandler rowRequestHandler; - private IScrollTableBody tBody; - private int firstvisible = 0; - private boolean sortAscending; - private String sortColumn; - private boolean columnReordering; - - /** - * This map contains captions and icon urls for actions like: * "33_c" -> - * "Edit" * "33_i" -> "http://dom.com/edit.png" - */ - private final HashMap actionMap = new HashMap(); - private String[] visibleColOrder; - private boolean initialContentReceived = false; - private Element scrollPositionElement; - private boolean enabled; - private boolean showColHeaders; - - /** flag to indicate that table body has changed */ - private boolean isNewBody = true; - - private boolean emitClickEvents; - - /* - * Read from the "recalcWidths" -attribute. When it is true, the table will - * recalculate the widths for columns - desirable in some cases. For #1983, - * marked experimental. - */ - boolean recalcWidths = false; - - private final ArrayList lazyUnregistryBag = new ArrayList(); - private String height; - private String width = ""; - private boolean rendering = false; - - public IScrollTable() { - bodyContainer.addScrollListener(this); - bodyContainer.setStyleName(CLASSNAME + "-body"); - - setStyleName(CLASSNAME); - add(tHead); - add(bodyContainer); - - rowRequestHandler = new RowRequestHandler(); - - } - - @SuppressWarnings("unchecked") - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - rendering = true; - if (client.updateComponent(this, uidl, true)) { - rendering = false; - return; - } - - // we may have pending cache row fetch, cancel it. See #2136 - rowRequestHandler.cancel(); - - enabled = !uidl.hasAttribute("disabled"); - - this.client = client; - paintableId = uidl.getStringAttribute("id"); - immediate = uidl.getBooleanAttribute("immediate"); - emitClickEvents = uidl.getBooleanAttribute("listenClicks"); - final int newTotalRows = uidl.getIntAttribute("totalrows"); - if (newTotalRows != totalRows) { - if (tBody != null) { - if (totalRows == 0) { - tHead.clear(); - } - initializedAndAttached = false; - initialContentReceived = false; - isNewBody = true; - } - totalRows = newTotalRows; - } - - recalcWidths = uidl.hasAttribute("recalcWidths"); - - pageLength = uidl.getIntAttribute("pagelength"); - if (pageLength == 0) { - pageLength = totalRows; - } - firstvisible = uidl.hasVariable("firstvisible") ? uidl - .getIntVariable("firstvisible") : 0; - if (firstvisible != lastRequestedFirstvisible && tBody != null) { - // received 'surprising' firstvisible from server: scroll there - firstRowInViewPort = firstvisible; - bodyContainer - .setScrollPosition(firstvisible * tBody.getRowHeight()); - } - - showRowHeaders = uidl.getBooleanAttribute("rowheaders"); - showColHeaders = uidl.getBooleanAttribute("colheaders"); - - if (uidl.hasVariable("sortascending")) { - sortAscending = uidl.getBooleanVariable("sortascending"); - sortColumn = uidl.getStringVariable("sortcolumn"); - } - - if (uidl.hasVariable("selected")) { - final Set selectedKeys = uidl - .getStringArrayVariableAsSet("selected"); - selectedRowKeys.clear(); - for (String string : selectedKeys) { - selectedRowKeys.add(string); - } - } - - if (uidl.hasAttribute("selectmode")) { - if (uidl.getBooleanAttribute("readonly")) { - selectMode = Table.SELECT_MODE_NONE; - } else if (uidl.getStringAttribute("selectmode").equals("multi")) { - selectMode = Table.SELECT_MODE_MULTI; - } else if (uidl.getStringAttribute("selectmode").equals("single")) { - selectMode = Table.SELECT_MODE_SINGLE; - } else { - selectMode = Table.SELECT_MODE_NONE; - } - } - - if (uidl.hasVariable("columnorder")) { - columnReordering = true; - columnOrder = uidl.getStringArrayVariable("columnorder"); - } - - if (uidl.hasVariable("collapsedcolumns")) { - tHead.setColumnCollapsingAllowed(true); - collapsedColumns = uidl - .getStringArrayVariableAsSet("collapsedcolumns"); - } else { - tHead.setColumnCollapsingAllowed(false); - } - - UIDL rowData = null; - for (final Iterator it = uidl.getChildIterator(); it.hasNext();) { - final UIDL c = (UIDL) it.next(); - if (c.getTag().equals("rows")) { - rowData = c; - } else if (c.getTag().equals("actions")) { - updateActionMap(c); - } else if (c.getTag().equals("visiblecolumns")) { - tHead.updateCellsFromUIDL(c); - } - } - updateHeader(uidl.getStringArrayAttribute("vcolorder")); - - if (!recalcWidths && initializedAndAttached) { - updateBody(rowData, uidl.getIntAttribute("firstrow"), uidl - .getIntAttribute("rows")); - if (headerChangedDuringUpdate) { - lazyAdjustColumnWidths.schedule(1); - } - } else { - if (tBody != null) { - tBody.removeFromParent(); - lazyUnregistryBag.add(tBody); - } - tBody = new IScrollTableBody(); - - tBody.renderInitialRows(rowData, uidl.getIntAttribute("firstrow"), - uidl.getIntAttribute("rows")); - bodyContainer.add(tBody); - initialContentReceived = true; - if (isAttached()) { - sizeInit(); - } - } - hideScrollPositionAnnotation(); - purgeUnregistryBag(); - rendering = false; - headerChangedDuringUpdate = false; - } - - /** - * Unregisters Paintables in "trashed" HasWidgets (IScrollTableBodys or - * IScrollTableRows). This is done lazily as Table must survive from - * "subtreecaching" logic. - */ - private void purgeUnregistryBag() { - for (Iterator iterator = lazyUnregistryBag.iterator(); iterator - .hasNext();) { - client.unregisterChildPaintables(iterator.next()); - } - lazyUnregistryBag.clear(); - } - - private void updateActionMap(UIDL c) { - final Iterator it = c.getChildIterator(); - while (it.hasNext()) { - final UIDL action = (UIDL) it.next(); - final String key = action.getStringAttribute("key"); - final String caption = action.getStringAttribute("caption"); - actionMap.put(key + "_c", caption); - if (action.hasAttribute("icon")) { - // TODO need some uri handling ?? - actionMap.put(key + "_i", client.translateToolkitUri(action - .getStringAttribute("icon"))); - } - } - - } - - public String getActionCaption(String actionKey) { - return actionMap.get(actionKey + "_c"); - } - - public String getActionIcon(String actionKey) { - return actionMap.get(actionKey + "_i"); - } - - private void updateHeader(String[] strings) { - if (strings == null) { - return; - } - - int visibleCols = strings.length; - int colIndex = 0; - if (showRowHeaders) { - tHead.enableColumn("0", colIndex); - visibleCols++; - visibleColOrder = new String[visibleCols]; - visibleColOrder[colIndex] = "0"; - colIndex++; - } else { - visibleColOrder = new String[visibleCols]; - tHead.removeCell("0"); - } - - int i; - for (i = 0; i < strings.length; i++) { - final String cid = strings[i]; - visibleColOrder[colIndex] = cid; - tHead.enableColumn(cid, colIndex); - colIndex++; - } - - tHead.setVisible(showColHeaders); - - } - - /** - * @param uidl - * which contains row data - * @param firstRow - * first row in data set - * @param reqRows - * amount of rows in data set - */ - private void updateBody(UIDL uidl, int firstRow, int reqRows) { - if (uidl == null || reqRows < 1) { - // container is empty, remove possibly existing rows - if (firstRow < 0) { - while (tBody.getLastRendered() > tBody.firstRendered) { - tBody.unlinkRow(false); - } - tBody.unlinkRow(false); - } - return; - } - - tBody.renderRows(uidl, firstRow, reqRows); - - final int optimalFirstRow = (int) (firstRowInViewPort - pageLength - * CACHE_RATE); - boolean cont = true; - while (cont && tBody.getLastRendered() > optimalFirstRow - && tBody.getFirstRendered() < optimalFirstRow) { - // client.console.log("removing row from start"); - cont = tBody.unlinkRow(true); - } - final int optimalLastRow = (int) (firstRowInViewPort + pageLength + pageLength - * CACHE_RATE); - cont = true; - while (cont && tBody.getLastRendered() > optimalLastRow) { - // client.console.log("removing row from the end"); - cont = tBody.unlinkRow(false); - } - tBody.fixSpacers(); - - } - - /** - * Gives correct column index for given column key ("cid" in UIDL). - * - * @param colKey - * @return column index of visible columns, -1 if column not visible - */ - private int getColIndexByKey(String colKey) { - // return 0 if asked for rowHeaders - if ("0".equals(colKey)) { - return 0; - } - for (int i = 0; i < visibleColOrder.length; i++) { - if (visibleColOrder[i].equals(colKey)) { - return i; - } - } - return -1; - } - - private boolean isCollapsedColumn(String colKey) { - if (collapsedColumns == null) { - return false; - } - if (collapsedColumns.contains(colKey)) { - return true; - } - return false; - } - - private String getColKeyByIndex(int index) { - return tHead.getHeaderCell(index).getColKey(); - } - - private void setColWidth(int colIndex, int w, boolean isDefinedWidth) { - final HeaderCell cell = tHead.getHeaderCell(colIndex); - cell.setWidth(w, isDefinedWidth); - tBody.setColWidth(colIndex, w); - } - - private int getColWidth(String colKey) { - return tHead.getHeaderCell(colKey).getWidth(); - } - - private IScrollTableRow getRenderedRowByKey(String key) { - final Iterator it = tBody.iterator(); - IScrollTableRow r = null; - while (it.hasNext()) { - r = (IScrollTableRow) it.next(); - if (r.getKey().equals(key)) { - return r; - } - } - return null; - } - - private void reOrderColumn(String columnKey, int newIndex) { - - final int oldIndex = getColIndexByKey(columnKey); - - // Change header order - tHead.moveCell(oldIndex, newIndex); - - // Change body order - tBody.moveCol(oldIndex, newIndex); - - /* - * Build new columnOrder and update it to server Note that columnOrder - * also contains collapsed columns so we cannot directly build it from - * cells vector Loop the old columnOrder and append in order to new - * array unless on moved columnKey. On new index also put the moved key - * i == index on columnOrder, j == index on newOrder - */ - final String oldKeyOnNewIndex = visibleColOrder[newIndex]; - if (showRowHeaders) { - newIndex--; // columnOrder don't have rowHeader - } - // add back hidden rows, - for (int i = 0; i < columnOrder.length; i++) { - if (columnOrder[i].equals(oldKeyOnNewIndex)) { - break; // break loop at target - } - if (isCollapsedColumn(columnOrder[i])) { - newIndex++; - } - } - // finally we can build the new columnOrder for server - final String[] newOrder = new String[columnOrder.length]; - for (int i = 0, j = 0; j < newOrder.length; i++) { - if (j == newIndex) { - newOrder[j] = columnKey; - j++; - } - if (i == columnOrder.length) { - break; - } - if (columnOrder[i].equals(columnKey)) { - continue; - } - newOrder[j] = columnOrder[i]; - j++; - } - columnOrder = newOrder; - // also update visibleColumnOrder - int i = showRowHeaders ? 1 : 0; - for (int j = 0; j < newOrder.length; j++) { - final String cid = newOrder[j]; - if (!isCollapsedColumn(cid)) { - visibleColOrder[i++] = cid; - } - } - client.updateVariable(paintableId, "columnorder", columnOrder, false); - } - - @Override - protected void onAttach() { - super.onAttach(); - if (initialContentReceived) { - sizeInit(); - } - } - - @Override - protected void onDetach() { - rowRequestHandler.cancel(); - super.onDetach(); - // ensure that scrollPosElement will be detached - if (scrollPositionElement != null) { - final Element parent = DOM.getParent(scrollPositionElement); - if (parent != null) { - DOM.removeChild(parent, scrollPositionElement); - } - } - } - - /** - * Run only once when component is attached and received its initial - * content. This function : * Syncs headers and bodys "natural widths and - * saves the values. * Sets proper width and height * Makes deferred request - * to get some cache rows - */ - private void sizeInit() { - /* - * We will use browsers table rendering algorithm to find proper column - * widths. If content and header take less space than available, we will - * divide extra space relatively to each column which has not width set. - * - * Overflow pixels are added to last column. - */ - - Iterator headCells = tHead.iterator(); - int i = 0; - int totalExplicitColumnsWidths = 0; - int total = 0; - float expandRatioDivider = 0; - - final int[] widths = new int[tHead.visibleCells.size()]; - - tHead.enableBrowserIntelligence(); - // first loop: collect natural widths - while (headCells.hasNext()) { - final HeaderCell hCell = (HeaderCell) headCells.next(); - int w = hCell.getWidth(); - if (hCell.isDefinedWidth()) { - // server has defined column width explicitly - totalExplicitColumnsWidths += w; - } else { - if (hCell.getExpandRatio() > 0) { - expandRatioDivider += hCell.getExpandRatio(); - w = 0; - } else { - // get and store greater of header width and column width, - // and - // store it as a minimumn natural col width - w = hCell.getNaturalColumnWidth(i); - } - hCell.setNaturalMinimumColumnWidth(w); - } - widths[i] = w; - total += w; - i++; - } - - tHead.disableBrowserIntelligence(); - - boolean willHaveScrollbarz = willHaveScrollbars(); - - // fix "natural" width if width not set - if (width == null || "".equals(width)) { - int w = total; - w += tBody.getCellExtraWidth() * visibleColOrder.length; - if (willHaveScrollbarz) { - w += Util.getNativeScrollbarSize(); - } - setContentWidth(w); - } - - int availW = tBody.getAvailableWidth(); - if (BrowserInfo.get().isIE()) { - // Hey IE, are you really sure about this? - availW = tBody.getAvailableWidth(); - } - availW -= tBody.getCellExtraWidth() * visibleColOrder.length; - - if (willHaveScrollbarz) { - availW -= Util.getNativeScrollbarSize(); - } - - boolean needsReLayout = false; - - if (availW > total) { - // natural size is smaller than available space - final int extraSpace = availW - total; - final int totalWidthR = total - totalExplicitColumnsWidths; - if (totalWidthR > 0) { - needsReLayout = true; - - if (expandRatioDivider > 0) { - // visible columns have some active expand ratios, excess - // space is divided according to them - headCells = tHead.iterator(); - i = 0; - while (headCells.hasNext()) { - HeaderCell hCell = (HeaderCell) headCells.next(); - if (hCell.getExpandRatio() > 0) { - int w = widths[i]; - final int newSpace = (int) (extraSpace * (hCell - .getExpandRatio() / expandRatioDivider)); - w += newSpace; - widths[i] = w; - } - i++; - } - } else { - // now we will share this sum relatively to those without - // explicit width - headCells = tHead.iterator(); - i = 0; - while (headCells.hasNext()) { - HeaderCell hCell = (HeaderCell) headCells.next(); - if (!hCell.isDefinedWidth()) { - int w = widths[i]; - final int newSpace = extraSpace * w / totalWidthR; - w += newSpace; - widths[i] = w; - } - i++; - } - } - } - - } else { - // bodys size will be more than available and scrollbar will appear - } - - // last loop: set possibly modified values or reset if new tBody - i = 0; - headCells = tHead.iterator(); - while (headCells.hasNext()) { - final HeaderCell hCell = (HeaderCell) headCells.next(); - if (isNewBody || hCell.getWidth() == -1) { - final int w = widths[i]; - setColWidth(i, w, false); - } - i++; - } - if (needsReLayout) { - tBody.reLayoutComponents(); - } - - /* - * Fix "natural" height if height is not set. This must be after width - * fixing so the components' widths have been adjusted. - */ - if (height == null || "".equals(height)) { - /* - * We must force an update of the row height as this point as it - * might have been (incorrectly) calculated earlier - */ - if (pageLength == totalRows) { - /* - * A hack to support variable height rows when paging is off. - * Generally this is not supported by scrolltable. We want to - * show all rows so the bodyHeight should be equal to the table - * height. - */ - int bodyHeight = tBody.getOffsetHeight(); - bodyContainer.setHeight(bodyHeight + "px"); - Util.runWebkitOverflowAutoFix(bodyContainer.getElement()); - } else { - int bodyHeight = (tBody.getRowHeight(true) * pageLength); - bodyContainer.setHeight(bodyHeight + "px"); - } - } - - isNewBody = false; - - if (firstvisible > 0) { - // Deferred due some Firefox oddities. IE & Safari could survive - // without - DeferredCommand.addCommand(new Command() { - public void execute() { - bodyContainer.setScrollPosition(firstvisible - * tBody.getRowHeight()); - firstRowInViewPort = firstvisible; - } - }); - } - - if (enabled) { - // Do we need cache rows - if (tBody.getLastRendered() + 1 < firstRowInViewPort + pageLength - + CACHE_REACT_RATE * pageLength) { - if (totalRows - 1 > tBody.getLastRendered()) { - // fetch cache rows - rowRequestHandler - .setReqFirstRow(tBody.getLastRendered() + 1); - rowRequestHandler - .setReqRows((int) (pageLength * CACHE_RATE)); - rowRequestHandler.deferRowFetch(1); - } - } - } - initializedAndAttached = true; - } - - private boolean willHaveScrollbars() { - if (!(height != null && !height.equals(""))) { - if (pageLength < totalRows) { - return true; - } - } else { - int fakeheight = tBody.getRowHeight() * totalRows; - int availableHeight = bodyContainer.getElement().getPropertyInt( - "clientHeight"); - if (fakeheight > availableHeight) { - return true; - } - } - return false; - } - - /** - * This method has logic which rows needs to be requested from server when - * user scrolls - */ - public void onScroll(Widget widget, int scrollLeft, int scrollTop) { - if (!initializedAndAttached) { - return; - } - if (!enabled) { - bodyContainer.setScrollPosition(firstRowInViewPort - * tBody.getRowHeight()); - return; - } - - rowRequestHandler.cancel(); - - // fix headers horizontal scrolling - tHead.setHorizontalScrollPosition(scrollLeft); - - firstRowInViewPort = (int) Math.ceil(scrollTop - / (double) tBody.getRowHeight()); - - int postLimit = (int) (firstRowInViewPort + pageLength + pageLength - * CACHE_REACT_RATE); - if (postLimit > totalRows - 1) { - postLimit = totalRows - 1; - } - int preLimit = (int) (firstRowInViewPort - pageLength - * CACHE_REACT_RATE); - if (preLimit < 0) { - preLimit = 0; - } - final int lastRendered = tBody.getLastRendered(); - final int firstRendered = tBody.getFirstRendered(); - - if (postLimit <= lastRendered && preLimit >= firstRendered) { - // remember which firstvisible we requested, in case the server has - // a differing opinion - lastRequestedFirstvisible = firstRowInViewPort; - client.updateVariable(paintableId, "firstvisible", - firstRowInViewPort, false); - return; // scrolled withing "non-react area" - } - - if (firstRowInViewPort - pageLength * CACHE_RATE > lastRendered - || firstRowInViewPort + pageLength + pageLength * CACHE_RATE < firstRendered) { - // need a totally new set - rowRequestHandler - .setReqFirstRow((int) (firstRowInViewPort - pageLength - * CACHE_RATE)); - int last = firstRowInViewPort + (int) CACHE_RATE * pageLength - + pageLength; - if (last > totalRows) { - last = totalRows - 1; - } - rowRequestHandler.setReqRows(last - - rowRequestHandler.getReqFirstRow() + 1); - rowRequestHandler.deferRowFetch(); - return; - } - if (preLimit < firstRendered) { - // need some rows to the beginning of the rendered area - rowRequestHandler - .setReqFirstRow((int) (firstRowInViewPort - pageLength - * CACHE_RATE)); - rowRequestHandler.setReqRows(firstRendered - - rowRequestHandler.getReqFirstRow()); - rowRequestHandler.deferRowFetch(); - - return; - } - if (postLimit > lastRendered) { - // need some rows to the end of the rendered area - rowRequestHandler.setReqFirstRow(lastRendered + 1); - rowRequestHandler.setReqRows((int) ((firstRowInViewPort - + pageLength + pageLength * CACHE_RATE) - lastRendered)); - rowRequestHandler.deferRowFetch(); - } - - } - - private void announceScrollPosition() { - if (scrollPositionElement == null) { - scrollPositionElement = DOM.createDiv(); - DOM.setElementProperty(scrollPositionElement, "className", - "i-table-scrollposition"); - DOM.appendChild(getElement(), scrollPositionElement); - } - - DOM.setStyleAttribute(scrollPositionElement, "position", "absolute"); - DOM.setStyleAttribute(scrollPositionElement, "marginLeft", (DOM - .getElementPropertyInt(getElement(), "offsetWidth") / 2 - 80) - + "px"); - DOM.setStyleAttribute(scrollPositionElement, "marginTop", -(DOM - .getElementPropertyInt(getElement(), "offsetHeight") - 2) - + "px"); - - // indexes go from 1-totalRows, as rowheaders in index-mode indicate - int last = (firstRowInViewPort + (bodyContainer.getOffsetHeight() / tBody - .getRowHeight())); - if (last > totalRows) { - last = totalRows; - } - DOM.setInnerHTML(scrollPositionElement, "" - + (firstRowInViewPort + 1) + " – " + last + "..." - + ""); - DOM.setStyleAttribute(scrollPositionElement, "display", "block"); - } - - private void hideScrollPositionAnnotation() { - if (scrollPositionElement != null) { - DOM.setStyleAttribute(scrollPositionElement, "display", "none"); - } - } - - private class RowRequestHandler extends Timer { - - private int reqFirstRow = 0; - private int reqRows = 0; - - public void deferRowFetch() { - deferRowFetch(250); - } - - public void deferRowFetch(int msec) { - if (reqRows > 0 && reqFirstRow < totalRows) { - schedule(msec); - - // tell scroll position to user if currently "visible" rows are - // not rendered - if ((firstRowInViewPort + pageLength > tBody.getLastRendered()) - || (firstRowInViewPort < tBody.getFirstRendered())) { - announceScrollPosition(); - } else { - hideScrollPositionAnnotation(); - } - } - } - - public void setReqFirstRow(int reqFirstRow) { - if (reqFirstRow < 0) { - reqFirstRow = 0; - } else if (reqFirstRow >= totalRows) { - reqFirstRow = totalRows - 1; - } - this.reqFirstRow = reqFirstRow; - } - - public void setReqRows(int reqRows) { - this.reqRows = reqRows; - } - - @Override - public void run() { - if (client.hasActiveRequest()) { - // if client connection is busy, don't bother loading it more - schedule(250); - - } else { - - int firstToBeRendered = tBody.firstRendered; - if (reqFirstRow < firstToBeRendered) { - firstToBeRendered = reqFirstRow; - } else if (firstRowInViewPort - (int) (CACHE_RATE * pageLength) > firstToBeRendered) { - firstToBeRendered = firstRowInViewPort - - (int) (CACHE_RATE * pageLength); - if (firstToBeRendered < 0) { - firstToBeRendered = 0; - } - } - - int lastToBeRendered = tBody.lastRendered; - - if (reqFirstRow + reqRows - 1 > lastToBeRendered) { - lastToBeRendered = reqFirstRow + reqRows - 1; - } else if (firstRowInViewPort + pageLength + pageLength - * CACHE_RATE < lastToBeRendered) { - lastToBeRendered = (firstRowInViewPort + pageLength + (int) (pageLength * CACHE_RATE)); - if (lastToBeRendered >= totalRows) { - lastToBeRendered = totalRows - 1; - } - // due Safari 3.1 bug (see #2607), verify reqrows, original - // problem unknown, but this should catch the issue - if (reqFirstRow + reqRows - 1 > lastToBeRendered) { - reqRows = lastToBeRendered - reqFirstRow; - } - } - - client.updateVariable(paintableId, "firstToBeRendered", - firstToBeRendered, false); - - client.updateVariable(paintableId, "lastToBeRendered", - lastToBeRendered, false); - // remember which firstvisible we requested, in case the server - // has - // a differing opinion - lastRequestedFirstvisible = firstRowInViewPort; - client.updateVariable(paintableId, "firstvisible", - firstRowInViewPort, false); - client.updateVariable(paintableId, "reqfirstrow", reqFirstRow, - false); - client.updateVariable(paintableId, "reqrows", reqRows, true); - - } - } - - public int getReqFirstRow() { - return reqFirstRow; - } - - public int getReqRows() { - return reqRows; - } - - /** - * Sends request to refresh content at this position. - */ - public void refreshContent() { - int first = (int) (firstRowInViewPort - pageLength * CACHE_RATE); - int reqRows = (int) (2 * pageLength * CACHE_RATE + pageLength); - if (first < 0) { - reqRows = reqRows + first; - first = 0; - } - setReqFirstRow(first); - setReqRows(reqRows); - run(); - } - } - - public class HeaderCell extends Widget { - - Element td = DOM.createTD(); - - Element captionContainer = DOM.createDiv(); - - Element colResizeWidget = DOM.createDiv(); - - Element floatingCopyOfHeaderCell; - - private boolean sortable = false; - private final String cid; - private boolean dragging; - - private int dragStartX; - private int colIndex; - private int originalWidth; - - private boolean isResizing; - - private int headerX; - - private boolean moved; - - private int closestSlot; - - private int width = -1; - - private int naturalWidth = -1; - - private char align = ALIGN_LEFT; - - boolean definedWidth = false; - - private float expandRatio = 0; - - public void setSortable(boolean b) { - sortable = b; - } - - public void setNaturalMinimumColumnWidth(int w) { - naturalWidth = w; - } - - public HeaderCell(String colId, String headerText) { - cid = colId; - - DOM.setElementProperty(colResizeWidget, "className", CLASSNAME - + "-resizer"); - DOM.sinkEvents(colResizeWidget, Event.MOUSEEVENTS); - - setText(headerText); - - DOM.appendChild(td, colResizeWidget); - - DOM.setElementProperty(captionContainer, "className", CLASSNAME - + "-caption-container"); - - // ensure no clipping initially (problem on column additions) - DOM.setStyleAttribute(captionContainer, "overflow", "visible"); - - DOM.sinkEvents(captionContainer, Event.MOUSEEVENTS); - - DOM.appendChild(td, captionContainer); - - DOM.sinkEvents(td, Event.MOUSEEVENTS); - - setElement(td); - } - - public void setWidth(int w, boolean ensureDefinedWidth) { - if (ensureDefinedWidth) { - definedWidth = true; - // on column resize expand ratio becomes zero - expandRatio = 0; - } - if (width == w) { - return; - } - if (width == -1) { - // go to default mode, clip content if necessary - DOM.setStyleAttribute(captionContainer, "overflow", ""); - } - width = w; - if (w == -1) { - DOM.setStyleAttribute(captionContainer, "width", ""); - setWidth(""); - } else { - - captionContainer.getStyle().setPropertyPx("width", w); - - /* - * if we already have tBody, set the header width properly, if - * not defer it. IE will fail with complex float in table header - * unless TD width is not explicitly set. - */ - if (tBody != null) { - int tdWidth = width + tBody.getCellExtraWidth(); - setWidth(tdWidth + "px"); - } else { - DeferredCommand.addCommand(new Command() { - public void execute() { - int tdWidth = width + tBody.getCellExtraWidth(); - setWidth(tdWidth + "px"); - } - }); - } - } - } - - public void setUndefinedWidth() { - definedWidth = false; - setWidth(-1, false); - } - - /** - * Detects if width is fixed by developer on server side or resized to - * current width by user. - * - * @return true if defined, false if "natural" width - */ - public boolean isDefinedWidth() { - return definedWidth; - } - - public int getWidth() { - return width; - } - - public void setText(String headerText) { - DOM.setInnerHTML(captionContainer, headerText); - } - - public String getColKey() { - return cid; - } - - private void setSorted(boolean sorted) { - if (sorted) { - if (sortAscending) { - this.setStyleName(CLASSNAME + "-header-cell-asc"); - } else { - this.setStyleName(CLASSNAME + "-header-cell-desc"); - } - } else { - this.setStyleName(CLASSNAME + "-header-cell"); - } - } - - /** - * Handle column reordering. - */ - @Override - public void onBrowserEvent(Event event) { - if (enabled && event != null) { - if (isResizing || event.getTarget() == colResizeWidget) { - onResizeEvent(event); - } else { - handleCaptionEvent(event); - } - } - } - - private void createFloatingCopy() { - floatingCopyOfHeaderCell = DOM.createDiv(); - DOM.setInnerHTML(floatingCopyOfHeaderCell, DOM.getInnerHTML(td)); - floatingCopyOfHeaderCell = DOM - .getChild(floatingCopyOfHeaderCell, 1); - DOM.setElementProperty(floatingCopyOfHeaderCell, "className", - CLASSNAME + "-header-drag"); - updateFloatingCopysPosition(DOM.getAbsoluteLeft(td), DOM - .getAbsoluteTop(td)); - DOM.appendChild(RootPanel.get().getElement(), - floatingCopyOfHeaderCell); - } - - private void updateFloatingCopysPosition(int x, int y) { - x -= DOM.getElementPropertyInt(floatingCopyOfHeaderCell, - "offsetWidth") / 2; - DOM.setStyleAttribute(floatingCopyOfHeaderCell, "left", x + "px"); - if (y > 0) { - DOM.setStyleAttribute(floatingCopyOfHeaderCell, "top", (y + 7) - + "px"); - } - } - - private void hideFloatingCopy() { - DOM.removeChild(RootPanel.get().getElement(), - floatingCopyOfHeaderCell); - floatingCopyOfHeaderCell = null; - } - - protected void handleCaptionEvent(Event event) { - switch (DOM.eventGetType(event)) { - case Event.ONMOUSEDOWN: - if (columnReordering) { - dragging = true; - moved = false; - colIndex = getColIndexByKey(cid); - DOM.setCapture(getElement()); - headerX = tHead.getAbsoluteLeft(); - DOM.eventPreventDefault(event); // prevent selecting text - } - break; - case Event.ONMOUSEUP: - if (columnReordering) { - dragging = false; - DOM.releaseCapture(getElement()); - if (moved) { - hideFloatingCopy(); - tHead.removeSlotFocus(); - if (closestSlot != colIndex - && closestSlot != (colIndex + 1)) { - if (closestSlot > colIndex) { - reOrderColumn(cid, closestSlot - 1); - } else { - reOrderColumn(cid, closestSlot); - } - } - } - } - - if (!moved) { - // mouse event was a click to header -> sort column - if (sortable) { - if (sortColumn.equals(cid)) { - // just toggle order - client.updateVariable(paintableId, "sortascending", - !sortAscending, false); - } else { - // set table scrolled by this column - client.updateVariable(paintableId, "sortcolumn", - cid, false); - } - // get also cache columns at the same request - bodyContainer.setScrollPosition(0); - firstvisible = 0; - rowRequestHandler.setReqFirstRow(0); - rowRequestHandler.setReqRows((int) (2 * pageLength - * CACHE_RATE + pageLength)); - rowRequestHandler.deferRowFetch(); - } - break; - } - break; - case Event.ONMOUSEMOVE: - if (dragging) { - if (!moved) { - createFloatingCopy(); - moved = true; - } - final int x = DOM.eventGetClientX(event) - + DOM.getElementPropertyInt(tHead.hTableWrapper, - "scrollLeft"); - int slotX = headerX; - closestSlot = colIndex; - int closestDistance = -1; - int start = 0; - if (showRowHeaders) { - start++; - } - final int visibleCellCount = tHead.getVisibleCellCount(); - for (int i = start; i <= visibleCellCount; i++) { - if (i > 0) { - final String colKey = getColKeyByIndex(i - 1); - slotX += getColWidth(colKey); - } - final int dist = Math.abs(x - slotX); - if (closestDistance == -1 || dist < closestDistance) { - closestDistance = dist; - closestSlot = i; - } - } - tHead.focusSlot(closestSlot); - - updateFloatingCopysPosition(DOM.eventGetClientX(event), -1); - } - break; - default: - break; - } - } - - private void onResizeEvent(Event event) { - switch (DOM.eventGetType(event)) { - case Event.ONMOUSEDOWN: - isResizing = true; - DOM.setCapture(getElement()); - dragStartX = DOM.eventGetClientX(event); - colIndex = getColIndexByKey(cid); - originalWidth = getWidth(); - DOM.eventPreventDefault(event); - break; - case Event.ONMOUSEUP: - isResizing = false; - DOM.releaseCapture(getElement()); - // readjust undefined width columns - lazyAdjustColumnWidths.cancel(); - lazyAdjustColumnWidths.schedule(1); - break; - case Event.ONMOUSEMOVE: - if (isResizing) { - final int deltaX = DOM.eventGetClientX(event) - dragStartX; - if (deltaX == 0) { - return; - } - - int newWidth = originalWidth + deltaX; - if (newWidth < tBody.getCellExtraWidth()) { - newWidth = tBody.getCellExtraWidth(); - } - setColWidth(colIndex, newWidth, true); - } - break; - default: - break; - } - } - - public String getCaption() { - return DOM.getInnerText(captionContainer); - } - - public boolean isEnabled() { - return getParent() != null; - } - - public void setAlign(char c) { - if (align != c) { - switch (c) { - case ALIGN_CENTER: - DOM.setStyleAttribute(captionContainer, "textAlign", - "center"); - break; - case ALIGN_RIGHT: - DOM.setStyleAttribute(captionContainer, "textAlign", - "right"); - break; - default: - DOM.setStyleAttribute(captionContainer, "textAlign", ""); - break; - } - } - align = c; - } - - public char getAlign() { - return align; - } - - /** - * Detects the natural minimum width for the column of this header cell. - * If column is resized by user or the width is defined by server the - * actual width is returned. Else the natural min width is returned. - * - * @param columnIndex - * column index hint, if -1 (unknown) it will be detected - * - * @return - */ - public int getNaturalColumnWidth(int columnIndex) { - if (isDefinedWidth()) { - return width; - } else { - if (naturalWidth < 0) { - // This is recently revealed column. Try to detect a proper - // value (greater of header and data - // cols) - - final int hw = ((Element) getElement().getLastChild()) - .getOffsetWidth() - + tBody.getCellExtraWidth(); - if (columnIndex < 0) { - columnIndex = 0; - for (Iterator it = tHead.iterator(); it - .hasNext(); columnIndex++) { - if (it.next() == this) { - break; - } - } - } - final int cw = tBody.getColWidth(columnIndex); - naturalWidth = (hw > cw ? hw : cw); - } - return naturalWidth; - } - } - - public void setExpandRatio(float floatAttribute) { - expandRatio = floatAttribute; - } - - public float getExpandRatio() { - return expandRatio; - } - - } - - /** - * HeaderCell that is header cell for row headers. - * - * Reordering disabled and clicking on it resets sorting. - */ - public class RowHeadersHeaderCell extends HeaderCell { - - RowHeadersHeaderCell() { - super("0", ""); - } - - @Override - protected void handleCaptionEvent(Event event) { - // NOP: RowHeaders cannot be reordered - // TODO It'd be nice to reset sorting here - } - } - - public class TableHead extends Panel implements ActionOwner { - - private static final int WRAPPER_WIDTH = 9000; - - Vector visibleCells = new Vector(); - - HashMap availableCells = new HashMap(); - - Element div = DOM.createDiv(); - Element hTableWrapper = DOM.createDiv(); - Element hTableContainer = DOM.createDiv(); - Element table = DOM.createTable(); - Element headerTableBody = DOM.createTBody(); - Element tr = DOM.createTR(); - - private final Element columnSelector = DOM.createDiv(); - - private int focusedSlot = -1; - - public TableHead() { - if (BrowserInfo.get().isIE()) { - table.setPropertyInt("cellSpacing", 0); - } - - DOM.setStyleAttribute(hTableWrapper, "overflow", "hidden"); - DOM.setElementProperty(hTableWrapper, "className", CLASSNAME - + "-header"); - - // TODO move styles to CSS - DOM.setElementProperty(columnSelector, "className", CLASSNAME - + "-column-selector"); - DOM.setStyleAttribute(columnSelector, "display", "none"); - - DOM.appendChild(table, headerTableBody); - DOM.appendChild(headerTableBody, tr); - DOM.appendChild(hTableContainer, table); - DOM.appendChild(hTableWrapper, hTableContainer); - DOM.appendChild(div, hTableWrapper); - DOM.appendChild(div, columnSelector); - setElement(div); - - setStyleName(CLASSNAME + "-header-wrap"); - - DOM.sinkEvents(columnSelector, Event.ONCLICK); - - availableCells.put("0", new RowHeadersHeaderCell()); - } - - @Override - public void clear() { - for (String cid : availableCells.keySet()) { - removeCell(cid); - } - availableCells.clear(); - availableCells.put("0", new RowHeadersHeaderCell()); - } - - public void updateCellsFromUIDL(UIDL uidl) { - Iterator it = uidl.getChildIterator(); - HashSet updated = new HashSet(); - updated.add("0"); - while (it.hasNext()) { - final UIDL col = (UIDL) it.next(); - final String cid = col.getStringAttribute("cid"); - updated.add(cid); - - String caption = buildCaptionHtmlSnippet(col); - HeaderCell c = getHeaderCell(cid); - if (c == null) { - c = new HeaderCell(cid, caption); - availableCells.put(cid, c); - if (initializedAndAttached) { - // we will need a column width recalculation - initializedAndAttached = false; - initialContentReceived = false; - isNewBody = true; - } - } else { - c.setText(caption); - } - - if (col.hasAttribute("sortable")) { - c.setSortable(true); - if (cid.equals(sortColumn)) { - c.setSorted(true); - } else { - c.setSorted(false); - } - } else { - c.setSortable(false); - } - - if (col.hasAttribute("align")) { - c.setAlign(col.getStringAttribute("align").charAt(0)); - } - if (col.hasAttribute("width")) { - final String width = col.getStringAttribute("width"); - c.setWidth(Integer.parseInt(width), true); - } else if (recalcWidths) { - c.setUndefinedWidth(); - } - if (col.hasAttribute("er")) { - c.setExpandRatio(col.getFloatAttribute("er")); - } - } - // check for orphaned header cells - for (String cid : availableCells.keySet()) { - if (!updated.contains(cid)) { - removeCell(cid); - it.remove(); - } - } - - } - - public void enableColumn(String cid, int index) { - final HeaderCell c = getHeaderCell(cid); - if (!c.isEnabled() || getHeaderCell(index) != c) { - setHeaderCell(index, c); - if (initializedAndAttached) { - headerChangedDuringUpdate = true; - } - } - } - - public int getVisibleCellCount() { - return visibleCells.size(); - } - - public void setHorizontalScrollPosition(int scrollLeft) { - DOM.setElementPropertyInt(hTableWrapper, "scrollLeft", scrollLeft); - } - - public void setColumnCollapsingAllowed(boolean cc) { - if (cc) { - DOM.setStyleAttribute(columnSelector, "display", "block"); - } else { - DOM.setStyleAttribute(columnSelector, "display", "none"); - } - } - - public void disableBrowserIntelligence() { - DOM.setStyleAttribute(hTableContainer, "width", WRAPPER_WIDTH - + "px"); - } - - public void enableBrowserIntelligence() { - DOM.setStyleAttribute(hTableContainer, "width", ""); - } - - public void setHeaderCell(int index, HeaderCell cell) { - if (cell.isEnabled()) { - // we're moving the cell - DOM.removeChild(tr, cell.getElement()); - orphan(cell); - } - if (index < visibleCells.size()) { - // insert to right slot - DOM.insertChild(tr, cell.getElement(), index); - adopt(cell); - visibleCells.insertElementAt(cell, index); - - } else if (index == visibleCells.size()) { - // simply append - DOM.appendChild(tr, cell.getElement()); - adopt(cell); - visibleCells.add(cell); - } else { - throw new RuntimeException( - "Header cells must be appended in order"); - } - } - - public HeaderCell getHeaderCell(int index) { - if (index < visibleCells.size()) { - return (HeaderCell) visibleCells.get(index); - } else { - return null; - } - } - - /** - * Get's HeaderCell by it's column Key. - * - * Note that this returns HeaderCell even if it is currently collapsed. - * - * @param cid - * Column key of accessed HeaderCell - * @return HeaderCell - */ - public HeaderCell getHeaderCell(String cid) { - return availableCells.get(cid); - } - - public void moveCell(int oldIndex, int newIndex) { - final HeaderCell hCell = getHeaderCell(oldIndex); - final Element cell = hCell.getElement(); - - visibleCells.remove(oldIndex); - DOM.removeChild(tr, cell); - - DOM.insertChild(tr, cell, newIndex); - visibleCells.insertElementAt(hCell, newIndex); - } - - public Iterator iterator() { - return visibleCells.iterator(); - } - - @Override - public boolean remove(Widget w) { - if (visibleCells.contains(w)) { - visibleCells.remove(w); - orphan(w); - DOM.removeChild(DOM.getParent(w.getElement()), w.getElement()); - return true; - } - return false; - } - - public void removeCell(String colKey) { - final HeaderCell c = getHeaderCell(colKey); - remove(c); - } - - private void focusSlot(int index) { - removeSlotFocus(); - if (index > 0) { - DOM.setElementProperty(DOM.getFirstChild(DOM.getChild(tr, - index - 1)), "className", CLASSNAME + "-resizer " - + CLASSNAME + "-focus-slot-right"); - } else { - DOM.setElementProperty(DOM.getFirstChild(DOM - .getChild(tr, index)), "className", CLASSNAME - + "-resizer " + CLASSNAME + "-focus-slot-left"); - } - focusedSlot = index; - } - - private void removeSlotFocus() { - if (focusedSlot < 0) { - return; - } - if (focusedSlot == 0) { - DOM.setElementProperty(DOM.getFirstChild(DOM.getChild(tr, - focusedSlot)), "className", CLASSNAME + "-resizer"); - } else if (focusedSlot > 0) { - DOM.setElementProperty(DOM.getFirstChild(DOM.getChild(tr, - focusedSlot - 1)), "className", CLASSNAME + "-resizer"); - } - focusedSlot = -1; - } - - @Override - public void onBrowserEvent(Event event) { - if (enabled) { - if (event.getTarget() == columnSelector) { - final int left = DOM.getAbsoluteLeft(columnSelector); - final int top = DOM.getAbsoluteTop(columnSelector) - + DOM.getElementPropertyInt(columnSelector, - "offsetHeight"); - client.getContextMenu().showAt(this, left, top); - } - } - } - - class VisibleColumnAction extends Action { - - String colKey; - private boolean collapsed; - - public VisibleColumnAction(String colKey) { - super(IScrollTable.TableHead.this); - this.colKey = colKey; - caption = tHead.getHeaderCell(colKey).getCaption(); - } - - @Override - public void execute() { - client.getContextMenu().hide(); - // toggle selected column - if (collapsedColumns.contains(colKey)) { - collapsedColumns.remove(colKey); - } else { - tHead.removeCell(colKey); - collapsedColumns.add(colKey); - lazyAdjustColumnWidths.schedule(1); - } - - // update variable to server - client.updateVariable(paintableId, "collapsedcolumns", - collapsedColumns.toArray(), false); - // let rowRequestHandler determine proper rows - rowRequestHandler.refreshContent(); - } - - public void setCollapsed(boolean b) { - collapsed = b; - } - - /** - * Override default method to distinguish on/off columns - */ - @Override - public String getHTML() { - final StringBuffer buf = new StringBuffer(); - if (collapsed) { - buf.append(""); - } else { - buf.append(""); - } - buf.append(super.getHTML()); - buf.append(""); - - return buf.toString(); - } - - } - - /* - * Returns columns as Action array for column select popup - */ - public Action[] getActions() { - Object[] cols; - if (columnReordering) { - cols = columnOrder; - } else { - // if columnReordering is disabled, we need different way to get - // all available columns - cols = visibleColOrder; - cols = new Object[visibleColOrder.length - + collapsedColumns.size()]; - int i; - for (i = 0; i < visibleColOrder.length; i++) { - cols[i] = visibleColOrder[i]; - } - for (final Iterator it = collapsedColumns.iterator(); it - .hasNext();) { - cols[i++] = it.next(); - } - } - final Action[] actions = new Action[cols.length]; - - for (int i = 0; i < cols.length; i++) { - final String cid = (String) cols[i]; - final HeaderCell c = getHeaderCell(cid); - final VisibleColumnAction a = new VisibleColumnAction(c - .getColKey()); - a.setCaption(c.getCaption()); - if (!c.isEnabled()) { - a.setCollapsed(true); - } - actions[i] = a; - } - return actions; - } - - public ApplicationConnection getClient() { - return client; - } - - public String getPaintableId() { - return paintableId; - } - - /** - * Returns column alignments for visible columns - */ - public char[] getColumnAlignments() { - final Iterator it = visibleCells.iterator(); - final char[] aligns = new char[visibleCells.size()]; - int colIndex = 0; - while (it.hasNext()) { - aligns[colIndex++] = ((HeaderCell) it.next()).getAlign(); - } - return aligns; - } - - } - - /** - * This Panel can only contain IScrollTableRow type of widgets. This - * "simulates" very large table, keeping spacers which take room of - * unrendered rows. - * - */ - public class IScrollTableBody extends Panel { - - public static final int DEFAULT_ROW_HEIGHT = 24; - - private int rowHeight = -1; - - private final List renderedRows = new Vector(); - - /** - * Due some optimizations row height measuring is deferred and initial - * set of rows is rendered detached. Flag set on when table body has - * been attached in dom and rowheight has been measured. - */ - private boolean tBodyMeasurementsDone = false; - - Element preSpacer = DOM.createDiv(); - Element postSpacer = DOM.createDiv(); - - Element container = DOM.createDiv(); - - TableSectionElement tBodyElement = Document.get().createTBodyElement(); - Element table = DOM.createTable(); - - private int firstRendered; - - private int lastRendered; - - private char[] aligns; - - IScrollTableBody() { - constructDOM(); - - setElement(container); - } - - private void constructDOM() { - DOM.setElementProperty(table, "className", CLASSNAME + "-table"); - if (BrowserInfo.get().isIE()) { - table.setPropertyInt("cellSpacing", 0); - } - DOM.setElementProperty(preSpacer, "className", CLASSNAME - + "-row-spacer"); - DOM.setElementProperty(postSpacer, "className", CLASSNAME - + "-row-spacer"); - - table.appendChild(tBodyElement); - DOM.appendChild(container, preSpacer); - DOM.appendChild(container, table); - DOM.appendChild(container, postSpacer); - - } - - public int getAvailableWidth() { - int availW = bodyContainer.getOffsetWidth() - getBorderWidth(); - return availW; - } - - public void renderInitialRows(UIDL rowData, int firstIndex, int rows) { - firstRendered = firstIndex; - lastRendered = firstIndex + rows - 1; - final Iterator it = rowData.getChildIterator(); - aligns = tHead.getColumnAlignments(); - while (it.hasNext()) { - final IScrollTableRow row = new IScrollTableRow((UIDL) it - .next(), aligns); - addRow(row); - } - if (isAttached()) { - fixSpacers(); - } - } - - public void renderRows(UIDL rowData, int firstIndex, int rows) { - // FIXME REVIEW - aligns = tHead.getColumnAlignments(); - final Iterator it = rowData.getChildIterator(); - if (firstIndex == lastRendered + 1) { - while (it.hasNext()) { - final IScrollTableRow row = createRow((UIDL) it.next()); - addRow(row); - lastRendered++; - } - fixSpacers(); - } else if (firstIndex + rows == firstRendered) { - final IScrollTableRow[] rowArray = new IScrollTableRow[rows]; - int i = rows; - while (it.hasNext()) { - i--; - rowArray[i] = createRow((UIDL) it.next()); - } - for (i = 0; i < rows; i++) { - addRowBeforeFirstRendered(rowArray[i]); - firstRendered--; - } - } else { - // completely new set of rows - while (lastRendered + 1 > firstRendered) { - unlinkRow(false); - } - final IScrollTableRow row = createRow((UIDL) it.next()); - firstRendered = firstIndex; - lastRendered = firstIndex - 1; - addRow(row); - lastRendered++; - setContainerHeight(); - fixSpacers(); - while (it.hasNext()) { - addRow(createRow((UIDL) it.next())); - lastRendered++; - } - fixSpacers(); - } - // this may be a new set of rows due content change, - // ensure we have proper cache rows - int reactFirstRow = (int) (firstRowInViewPort - pageLength - * CACHE_REACT_RATE); - int reactLastRow = (int) (firstRowInViewPort + pageLength + pageLength - * CACHE_REACT_RATE); - if (reactFirstRow < 0) { - reactFirstRow = 0; - } - if (reactLastRow > totalRows) { - reactLastRow = totalRows - 1; - } - if (lastRendered < reactLastRow) { - // get some cache rows below visible area - rowRequestHandler.setReqFirstRow(lastRendered + 1); - rowRequestHandler.setReqRows(reactLastRow - lastRendered - 1); - rowRequestHandler.deferRowFetch(1); - } else if (tBody.getFirstRendered() > reactFirstRow) { - /* - * Branch for fetching cache above visible area. - * - * If cache needed for both before and after visible area, this - * will be rendered after-cache is reveived and rendered. So in - * some rare situations table may take two cache visits to - * server. - */ - rowRequestHandler.setReqFirstRow(reactFirstRow); - rowRequestHandler.setReqRows(firstRendered - reactFirstRow); - rowRequestHandler.deferRowFetch(1); - } - } - - /** - * This method is used to instantiate new rows for this table. It - * automatically sets correct widths to rows cells and assigns correct - * client reference for child widgets. - * - * This method can be called only after table has been initialized - * - * @param uidl - */ - private IScrollTableRow createRow(UIDL uidl) { - final IScrollTableRow row = new IScrollTableRow(uidl, aligns); - final int cells = DOM.getChildCount(row.getElement()); - for (int i = 0; i < cells; i++) { - final Element cell = DOM.getChild(row.getElement(), i); - int w = IScrollTable.this.getColWidth(getColKeyByIndex(i)); - if (w < 0) { - w = 0; - } - cell.getFirstChildElement().getStyle() - .setPropertyPx("width", w); - cell.getStyle().setPropertyPx("width", w); - } - return row; - } - - private void addRowBeforeFirstRendered(IScrollTableRow row) { - IScrollTableRow first = null; - if (renderedRows.size() > 0) { - first = (IScrollTableRow) renderedRows.get(0); - } - if (first != null && first.getStyleName().indexOf("-odd") == -1) { - row.addStyleName(CLASSNAME + "-row-odd"); - } else { - row.addStyleName(CLASSNAME + "-row"); - } - if (row.isSelected()) { - row.addStyleName("i-selected"); - } - tBodyElement.insertBefore(row.getElement(), tBodyElement - .getFirstChild()); - adopt(row); - renderedRows.add(0, row); - } - - private void addRow(IScrollTableRow row) { - IScrollTableRow last = null; - if (renderedRows.size() > 0) { - last = (IScrollTableRow) renderedRows - .get(renderedRows.size() - 1); - } - if (last != null && last.getStyleName().indexOf("-odd") == -1) { - row.addStyleName(CLASSNAME + "-row-odd"); - } else { - row.addStyleName(CLASSNAME + "-row"); - } - if (row.isSelected()) { - row.addStyleName("i-selected"); - } - tBodyElement.appendChild(row.getElement()); - adopt(row); - renderedRows.add(row); - } - - public Iterator iterator() { - return renderedRows.iterator(); - } - - /** - * @return false if couldn't remove row - */ - public boolean unlinkRow(boolean fromBeginning) { - if (lastRendered - firstRendered < 0) { - return false; - } - int index; - if (fromBeginning) { - index = 0; - firstRendered++; - } else { - index = renderedRows.size() - 1; - lastRendered--; - } - if (index >= 0) { - final IScrollTableRow toBeRemoved = (IScrollTableRow) renderedRows - .get(index); - lazyUnregistryBag.add(toBeRemoved); - tBodyElement.removeChild(toBeRemoved.getElement()); - orphan(toBeRemoved); - renderedRows.remove(index); - fixSpacers(); - return true; - } else { - return false; - } - } - - @Override - public boolean remove(Widget w) { - throw new UnsupportedOperationException(); - } - - @Override - protected void onAttach() { - super.onAttach(); - setContainerHeight(); - } - - /** - * Fix container blocks height according to totalRows to avoid - * "bouncing" when scrolling - */ - private void setContainerHeight() { - fixSpacers(); - DOM.setStyleAttribute(container, "height", totalRows - * getRowHeight() + "px"); - } - - private void fixSpacers() { - int prepx = getRowHeight() * firstRendered; - if (prepx < 0) { - prepx = 0; - } - DOM.setStyleAttribute(preSpacer, "height", prepx + "px"); - int postpx = getRowHeight() * (totalRows - 1 - lastRendered); - if (postpx < 0) { - postpx = 0; - } - DOM.setStyleAttribute(postSpacer, "height", postpx + "px"); - } - - public int getRowHeight() { - return getRowHeight(false); - } - - public int getRowHeight(boolean forceUpdate) { - if (tBodyMeasurementsDone && !forceUpdate) { - return rowHeight; - } else { - - if (tBodyElement.getRows().getLength() > 0) { - rowHeight = getTableHeight() - / tBodyElement.getRows().getLength(); - } else { - if (isAttached()) { - // measure row height by adding a dummy row - IScrollTableRow scrollTableRow = new IScrollTableRow(); - tBodyElement.appendChild(scrollTableRow.getElement()); - getRowHeight(forceUpdate); - tBodyElement.removeChild(scrollTableRow.getElement()); - } else { - // TODO investigate if this can never happen anymore - return DEFAULT_ROW_HEIGHT; - } - } - tBodyMeasurementsDone = true; - return rowHeight; - } - } - - public int getTableHeight() { - return table.getOffsetHeight(); - } - - /** - * Returns the width available for column content. - * - * @param columnIndex - * @return - */ - public int getColWidth(int columnIndex) { - if (tBodyMeasurementsDone) { - NodeList rows = tBodyElement.getRows(); - if (rows.getLength() == 0) { - // no rows yet rendered - return 0; - } else { - com.google.gwt.dom.client.Element wrapperdiv = rows - .getItem(0).getCells().getItem(columnIndex) - .getFirstChildElement(); - return wrapperdiv.getOffsetWidth(); - } - } else { - return 0; - } - } - - /** - * Sets the content width of a column. - * - * Due IE limitation, we must set the width to a wrapper elements inside - * table cells (with overflow hidden, which does not work on td - * elements). - * - * To get this work properly crossplatform, we will also set the width - * of td. - * - * @param colIndex - * @param w - */ - public void setColWidth(int colIndex, int w) { - NodeList rows2 = tBodyElement.getRows(); - final int rows = rows2.getLength(); - for (int i = 0; i < rows; i++) { - TableRowElement row = rows2.getItem(i); - TableCellElement cell = row.getCells().getItem(colIndex); - cell.getFirstChildElement().getStyle() - .setPropertyPx("width", w); - cell.getStyle().setPropertyPx("width", w); - } - } - - private int cellExtraWidth = -1; - - /** - * Method to return the space used for cell paddings + border. - */ - private int getCellExtraWidth() { - if (cellExtraWidth < 0) { - detectExtrawidth(); - } - return cellExtraWidth; - } - - private void detectExtrawidth() { - NodeList rows = tBodyElement.getRows(); - if (rows.getLength() == 0) { - /* need to temporary add empty row and detect */ - IScrollTableRow scrollTableRow = new IScrollTableRow(); - tBodyElement.appendChild(scrollTableRow.getElement()); - detectExtrawidth(); - tBodyElement.removeChild(scrollTableRow.getElement()); - } else { - TableRowElement item = rows.getItem(0); - TableCellElement firstTD = item.getCells().getItem(0); - com.google.gwt.dom.client.Element wrapper = firstTD - .getFirstChildElement(); - cellExtraWidth = firstTD.getOffsetWidth() - - wrapper.getOffsetWidth(); - } - } - - private void reLayoutComponents() { - for (Widget w : this) { - IScrollTableRow r = (IScrollTableRow) w; - for (Widget widget : r) { - client.handleComponentRelativeSize(widget); - } - } - } - - public int getLastRendered() { - return lastRendered; - } - - public int getFirstRendered() { - return firstRendered; - } - - public void moveCol(int oldIndex, int newIndex) { - - // loop all rows and move given index to its new place - final Iterator rows = iterator(); - while (rows.hasNext()) { - final IScrollTableRow row = (IScrollTableRow) rows.next(); - - final Element td = DOM.getChild(row.getElement(), oldIndex); - DOM.removeChild(row.getElement(), td); - - DOM.insertChild(row.getElement(), td, newIndex); - - } - - } - - public class IScrollTableRow extends Panel implements ActionOwner, - Container { - - Vector childWidgets = new Vector(); - private boolean selected = false; - private final int rowKey; - private List pendingComponentPaints; - - private String[] actionKeys = null; - private TableRowElement rowElement; - - private IScrollTableRow(int rowKey) { - this.rowKey = rowKey; - rowElement = Document.get().createTRElement(); - setElement(rowElement); - DOM.sinkEvents(getElement(), Event.ONCLICK | Event.ONDBLCLICK - | Event.ONCONTEXTMENU); - } - - private void paintComponent(Paintable p, UIDL uidl) { - if (isAttached()) { - p.updateFromUIDL(uidl, client); - } else { - if (pendingComponentPaints == null) { - pendingComponentPaints = new LinkedList(); - } - pendingComponentPaints.add(uidl); - } - } - - @Override - protected void onAttach() { - super.onAttach(); - if (pendingComponentPaints != null) { - for (UIDL uidl : pendingComponentPaints) { - Paintable paintable = client.getPaintable(uidl); - paintable.updateFromUIDL(uidl, client); - } - } - } - - public String getKey() { - return String.valueOf(rowKey); - } - - public IScrollTableRow(UIDL uidl, char[] aligns) { - this(uidl.getIntAttribute("key")); - - String rowStyle = uidl.getStringAttribute("rowstyle"); - if (rowStyle != null) { - addStyleName(CLASSNAME + "-row-" + rowStyle); - } - - tHead.getColumnAlignments(); - int col = 0; - int visibleColumnIndex = -1; - - // row header - if (showRowHeaders) { - addCell(buildCaptionHtmlSnippet(uidl), aligns[col++], "", - true); - } - - if (uidl.hasAttribute("al")) { - actionKeys = uidl.getStringArrayAttribute("al"); - } - - final Iterator cells = uidl.getChildIterator(); - while (cells.hasNext()) { - final Object cell = cells.next(); - visibleColumnIndex++; - - String columnId = visibleColOrder[visibleColumnIndex]; - - String style = ""; - if (uidl.hasAttribute("style-" + columnId)) { - style = uidl.getStringAttribute("style-" + columnId); - } - - if (cell instanceof String) { - addCell(cell.toString(), aligns[col++], style, false); - } else { - final Paintable cellContent = client - .getPaintable((UIDL) cell); - - addCell((Widget) cellContent, aligns[col++], style); - paintComponent(cellContent, (UIDL) cell); - } - } - if (uidl.hasAttribute("selected") && !isSelected()) { - toggleSelection(); - } - } - - /** - * Add a dummy row, used for measurements if Table is empty. - */ - public IScrollTableRow() { - this(0); - addStyleName(CLASSNAME + "-row"); - addCell("_", 'b', "", true); - } - - public void addCell(String text, char align, String style, - boolean textIsHTML) { - // String only content is optimized by not using Label widget - final Element td = DOM.createTD(); - final Element container = DOM.createDiv(); - String className = CLASSNAME + "-cell-content"; - if (style != null && !style.equals("")) { - className += " " + CLASSNAME + "-cell-content-" + style; - } - td.setClassName(className); - container.setClassName(CLASSNAME + "-cell-wrapper"); - if (textIsHTML) { - container.setInnerHTML(text); - } else { - container.setInnerText(text); - } - if (align != ALIGN_LEFT) { - switch (align) { - case ALIGN_CENTER: - container.getStyle().setProperty("textAlign", "center"); - break; - case ALIGN_RIGHT: - default: - container.getStyle().setProperty("textAlign", "right"); - break; - } - } - td.appendChild(container); - getElement().appendChild(td); - } - - public void addCell(Widget w, char align, String style) { - final Element td = DOM.createTD(); - final Element container = DOM.createDiv(); - String className = CLASSNAME + "-cell-content"; - if (style != null && !style.equals("")) { - className += " " + CLASSNAME + "-cell-content-" + style; - } - td.setClassName(className); - container.setClassName(CLASSNAME + "-cell-wrapper"); - // TODO most components work with this, but not all (e.g. - // Select) - // Old comment: make widget cells respect align. - // text-align:center for IE, margin: auto for others - if (align != ALIGN_LEFT) { - switch (align) { - case ALIGN_CENTER: - container.getStyle().setProperty("textAlign", "center"); - break; - case ALIGN_RIGHT: - default: - container.getStyle().setProperty("textAlign", "right"); - break; - } - } - td.appendChild(container); - getElement().appendChild(td); - // ensure widget not attached to another element (possible tBody - // change) - w.removeFromParent(); - container.appendChild(w.getElement()); - adopt(w); - childWidgets.add(w); - } - - public Iterator iterator() { - return childWidgets.iterator(); - } - - @Override - public boolean remove(Widget w) { - if (childWidgets.contains(w)) { - orphan(w); - DOM.removeChild(DOM.getParent(w.getElement()), w - .getElement()); - childWidgets.remove(w); - return true; - } else { - return false; - } - } - - private void handleClickEvent(Event event, Element targetTdOrTr) { - if (emitClickEvents) { - boolean doubleClick = (DOM.eventGetType(event) == Event.ONDBLCLICK); - - /* This row was clicked */ - client.updateVariable(paintableId, "clickedKey", "" - + rowKey, false); - - if (getElement() == targetTdOrTr.getParentElement()) { - /* A specific column was clicked */ - int childIndex = DOM.getChildIndex(getElement(), - targetTdOrTr); - String colKey = null; - colKey = tHead.getHeaderCell(childIndex).getColKey(); - client.updateVariable(paintableId, "clickedColKey", - colKey, false); - } - - MouseEventDetails details = new MouseEventDetails(event); - // Note: the 'immediate' logic would need to be more - // involved (see #2104), but iscrolltable always sends - // select event, even though nullselectionallowed wont let - // the change trough. Will need to be updated if that is - // changed. - client - .updateVariable( - paintableId, - "clickEvent", - details.toString(), - !(!doubleClick - && selectMode > Table.SELECT_MODE_NONE && immediate)); - } - } - - /* - * React on click that occur on content cells only - */ - @Override - public void onBrowserEvent(Event event) { - if (enabled) { - Element targetTdOrTr = getEventTargetTdOrTr(event); - if (targetTdOrTr != null) { - switch (DOM.eventGetType(event)) { - case Event.ONCLICK: - handleClickEvent(event, targetTdOrTr); - if (selectMode > Table.SELECT_MODE_NONE) { - toggleSelection(); - // Note: changing the immediateness of this - // might - // require changes to "clickEvent" immediateness - // also. - client.updateVariable(paintableId, "selected", - selectedRowKeys.toArray(), immediate); - } - break; - case Event.ONDBLCLICK: - handleClickEvent(event, targetTdOrTr); - break; - case Event.ONCONTEXTMENU: - showContextMenu(event); - break; - default: - break; - } - } - } - super.onBrowserEvent(event); - } - - /** - * Finds the TD that the event interacts with. Returns null if the - * target of the event should not be handled. If the event target is - * the row directly this method returns the TR element instead of - * the TD. - * - * @param event - * @return TD or TR element that the event targets (the actual event - * target is this element or a child of it) - */ - private Element getEventTargetTdOrTr(Event event) { - Element targetTdOrTr = null; - - final Element eventTarget = DOM.eventGetTarget(event); - final Element eventTargetParent = DOM.getParent(eventTarget); - final Element eventTargetGrandParent = DOM - .getParent(eventTargetParent); - - final Element thisTrElement = getElement(); - - if (eventTarget == thisTrElement) { - // This was a click on the TR element - targetTdOrTr = eventTarget; - // rowTarget = true; - } else if (thisTrElement == eventTargetParent) { - // Target parent is the TR, so the actual target is the TD - targetTdOrTr = eventTarget; - } else if (thisTrElement == eventTargetGrandParent) { - // Target grand parent is the TR, so the parent is the TD - targetTdOrTr = eventTargetParent; - } else { - /* - * This is a workaround to make Labels and Embedded in a - * Table clickable (see #2688). It is really not a fix as it - * does not work for a custom component (not extending - * ILabel/IEmbedded) or for read only textfields etc. - */ - Element tdElement = eventTargetParent; - while (DOM.getParent(tdElement) != thisTrElement) { - tdElement = DOM.getParent(tdElement); - } - - Element componentElement = tdElement.getFirstChildElement() - .getFirstChildElement().cast(); - Widget widget = (Widget) client - .getPaintable(componentElement); - if (widget instanceof ILabel || widget instanceof IEmbedded) { - targetTdOrTr = tdElement; - } - } - - return targetTdOrTr; - } - - public void showContextMenu(Event event) { - if (enabled && actionKeys != null) { - int left = event.getClientX(); - int top = event.getClientY(); - top += Window.getScrollTop(); - left += Window.getScrollLeft(); - client.getContextMenu().showAt(this, left, top); - } - event.cancelBubble(true); - event.preventDefault(); - } - - public boolean isSelected() { - return selected; - } - - private void toggleSelection() { - selected = !selected; - if (selected) { - if (selectMode == Table.SELECT_MODE_SINGLE) { - deselectAll(); - } - selectedRowKeys.add(String.valueOf(rowKey)); - addStyleName("i-selected"); - } else { - selectedRowKeys.remove(String.valueOf(rowKey)); - removeStyleName("i-selected"); - } - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.terminal.gwt.client.ui.IActionOwner#getActions - * () - */ - public Action[] getActions() { - if (actionKeys == null) { - return new Action[] {}; - } - final Action[] actions = new Action[actionKeys.length]; - for (int i = 0; i < actions.length; i++) { - final String actionKey = actionKeys[i]; - final TreeAction a = new TreeAction(this, String - .valueOf(rowKey), actionKey); - a.setCaption(getActionCaption(actionKey)); - a.setIconUrl(getActionIcon(actionKey)); - actions[i] = a; - } - return actions; - } - - public ApplicationConnection getClient() { - return client; - } - - public String getPaintableId() { - return paintableId; - } - - public RenderSpace getAllocatedSpace(Widget child) { - int w = 0; - int i = getColIndexOf(child); - HeaderCell headerCell = tHead.getHeaderCell(i); - if (headerCell != null) { - if (initializedAndAttached) { - w = headerCell.getWidth(); - } else { - // header offset width is not absolutely correct value, - // but a best guess (expecting similar content in all - // columns -> - // if one component is relative width so are others) - w = headerCell.getOffsetWidth() - getCellExtraWidth(); - } - } - return new RenderSpace(w, getRowHeight()); - } - - private int getColIndexOf(Widget child) { - com.google.gwt.dom.client.Element widgetCell = child - .getElement().getParentElement().getParentElement(); - NodeList cells = rowElement.getCells(); - for (int i = 0; i < cells.getLength(); i++) { - if (cells.getItem(i) == widgetCell) { - return i; - } - } - return -1; - } - - public boolean hasChildComponent(Widget component) { - return childWidgets.contains(component); - } - - public void replaceChildComponent(Widget oldComponent, - Widget newComponent) { - com.google.gwt.dom.client.Element parentElement = oldComponent - .getElement().getParentElement(); - int index = childWidgets.indexOf(oldComponent); - oldComponent.removeFromParent(); - - parentElement.appendChild(newComponent.getElement()); - childWidgets.insertElementAt(newComponent, index); - adopt(newComponent); - - } - - public boolean requestLayout(Set children) { - // row size should never change and system wouldn't event - // survive as this is a kind of fake paitable - return true; - } - - public void updateCaption(Paintable component, UIDL uidl) { - // NOP, not rendered - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - // Should never be called, - // Component container interface faked here to get layouts - // render properly - } - } - } - - public void deselectAll() { - final Object[] keys = selectedRowKeys.toArray(); - for (int i = 0; i < keys.length; i++) { - final IScrollTableRow row = getRenderedRowByKey((String) keys[i]); - if (row != null && row.isSelected()) { - row.toggleSelection(); - } - } - // still ensure all selects are removed from (not necessary rendered) - selectedRowKeys.clear(); - - } - - @Override - public void setWidth(String width) { - if (this.width.equals(width)) { - return; - } - - this.width = width; - if (width != null && !"".equals(width)) { - super.setWidth(width); - int innerPixels = getOffsetWidth() - getBorderWidth(); - if (innerPixels < 0) { - innerPixels = 0; - } - setContentWidth(innerPixels); - - if (!rendering) { - // readjust undefined width columns - lazyAdjustColumnWidths.cancel(); - lazyAdjustColumnWidths.schedule(LAZY_COLUMN_ADJUST_TIMEOUT); - } - - } else { - super.setWidth(""); - } - - } - - private static final int LAZY_COLUMN_ADJUST_TIMEOUT = 300; - - private final Timer lazyAdjustColumnWidths = new Timer() { - /** - * Check for column widths, and available width, to see if we can fix - * column widths "optimally". Doing this lazily to avoid expensive - * calculation when resizing is not yet finished. - */ - @Override - public void run() { - - Iterator headCells = tHead.iterator(); - int usedMinimumWidth = 0; - int totalExplicitColumnsWidths = 0; - float expandRatioDivider = 0; - int colIndex = 0; - while (headCells.hasNext()) { - final HeaderCell hCell = (HeaderCell) headCells.next(); - if (hCell.isDefinedWidth()) { - totalExplicitColumnsWidths += hCell.getWidth(); - usedMinimumWidth += hCell.getWidth(); - } else { - usedMinimumWidth += hCell.getNaturalColumnWidth(colIndex); - expandRatioDivider += hCell.getExpandRatio(); - } - colIndex++; - } - - int availW = tBody.getAvailableWidth(); - // Hey IE, are you really sure about this? - availW = tBody.getAvailableWidth(); - availW -= tBody.getCellExtraWidth() * visibleColOrder.length; - if (willHaveScrollbars()) { - availW -= Util.getNativeScrollbarSize(); - } - - int extraSpace = availW - usedMinimumWidth; - if (extraSpace < 0) { - extraSpace = 0; - } - - int totalUndefinedNaturaWidths = usedMinimumWidth - - totalExplicitColumnsWidths; - - // we have some space that can be divided optimally - HeaderCell hCell; - colIndex = 0; - headCells = tHead.iterator(); - while (headCells.hasNext()) { - hCell = (HeaderCell) headCells.next(); - if (!hCell.isDefinedWidth()) { - int w = hCell.getNaturalColumnWidth(colIndex); - int newSpace; - if (expandRatioDivider > 0) { - // divide excess space by expand ratios - newSpace = (int) (w + extraSpace - * hCell.getExpandRatio() / expandRatioDivider); - } else { - if (totalUndefinedNaturaWidths != 0) { - // divide relatively to natural column widths - newSpace = w + extraSpace * w - / totalUndefinedNaturaWidths; - } else { - newSpace = w; - } - } - setColWidth(colIndex, newSpace, false); - } - colIndex++; - } - Util.runWebkitOverflowAutoFix(bodyContainer.getElement()); - tBody.reLayoutComponents(); - } - }; - - /** - * helper to set pixel size of head and body part - * - * @param pixels - */ - private void setContentWidth(int pixels) { - tHead.setWidth(pixels + "px"); - bodyContainer.setWidth(pixels + "px"); - } - - private int borderWidth = -1; - - /** - * @return border left + border right - */ - private int getBorderWidth() { - if (borderWidth < 0) { - borderWidth = Util.measureHorizontalPaddingAndBorder(bodyContainer - .getElement(), 2); - if (borderWidth < 0) { - borderWidth = 0; - } - } - return borderWidth; - } - - /** - * Ensures scrollable area is properly sized. - */ - private void setContainerHeight() { - if (height != null && !"".equals(height)) { - int contentH = getOffsetHeight() - tHead.getOffsetHeight(); - contentH -= getContentAreaBorderHeight(); - if (contentH < 0) { - contentH = 0; - } - bodyContainer.setHeight(contentH + "px"); - } - } - - private int contentAreaBorderHeight = -1; - - /** - * @return border top + border bottom of the scrollable area of table - */ - private int getContentAreaBorderHeight() { - if (contentAreaBorderHeight < 0) { - DOM.setStyleAttribute(bodyContainer.getElement(), "overflow", - "hidden"); - contentAreaBorderHeight = bodyContainer.getOffsetHeight() - - bodyContainer.getElement().getPropertyInt("clientHeight"); - DOM.setStyleAttribute(bodyContainer.getElement(), "overflow", - "auto"); - } - return contentAreaBorderHeight; - } - - @Override - public void setHeight(String height) { - this.height = height; - super.setHeight(height); - setContainerHeight(); - } - - /* - * Overridden due Table might not survive of visibility change (scroll pos - * lost). Example ITabPanel just set contained components invisible and back - * when changing tabs. - */ - @Override - public void setVisible(boolean visible) { - if (isVisible() != visible) { - super.setVisible(visible); - if (initializedAndAttached) { - if (visible) { - DeferredCommand.addCommand(new Command() { - public void execute() { - bodyContainer.setScrollPosition(firstRowInViewPort - * tBody.getRowHeight()); - } - }); - } - } - } - } - - /** - * Helper function to build html snippet for column or row headers - * - * @param uidl - * possibly with values caption and icon - * @return html snippet containing possibly an icon + caption text - */ - private String buildCaptionHtmlSnippet(UIDL uidl) { - String s = uidl.getStringAttribute("caption"); - if (uidl.hasAttribute("icon")) { - s = "\"icon\"" + s; - } - return s; - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ISlider.java b/src/com/vaadin/terminal/gwt/client/ui/ISlider.java deleted file mode 100644 index 0cd04f6e89..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ISlider.java +++ /dev/null @@ -1,436 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ -// -package com.vaadin.terminal.gwt.client.ui; - -import com.google.gwt.user.client.Command; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.DeferredCommand; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.ContainerResizedListener; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; - -public class ISlider extends Widget implements Paintable, Field, - ContainerResizedListener { - - public static final String CLASSNAME = "i-slider"; - - /** - * Minimum size (width or height, depending on orientation) of the slider - * base. - */ - private static final int MIN_SIZE = 50; - - ApplicationConnection client; - - String id; - - private boolean immediate; - private boolean disabled; - private boolean readonly; - private boolean scrollbarStyle; - - private int handleSize; - private double min; - private double max; - private int resolution; - private Double value; - private boolean vertical; - private int size = -1; - private boolean arrows; - - /* DOM element for slider's base */ - private final Element base; - private final int BASE_BORDER_WIDTH = 1; - - /* DOM element for slider's handle */ - private final Element handle; - - /* DOM element for decrement arrow */ - private final Element smaller; - - /* DOM element for increment arrow */ - private final Element bigger; - - /* Temporary dragging/animation variables */ - private boolean dragging = false; - - public ISlider() { - super(); - - setElement(DOM.createDiv()); - base = DOM.createDiv(); - handle = DOM.createDiv(); - smaller = DOM.createDiv(); - bigger = DOM.createDiv(); - - setStyleName(CLASSNAME); - DOM.setElementProperty(base, "className", CLASSNAME + "-base"); - DOM.setElementProperty(handle, "className", CLASSNAME + "-handle"); - DOM.setElementProperty(smaller, "className", CLASSNAME + "-smaller"); - DOM.setElementProperty(bigger, "className", CLASSNAME + "-bigger"); - - DOM.appendChild(getElement(), bigger); - DOM.appendChild(getElement(), smaller); - DOM.appendChild(getElement(), base); - DOM.appendChild(base, handle); - - // Hide initially - DOM.setStyleAttribute(smaller, "display", "none"); - DOM.setStyleAttribute(bigger, "display", "none"); - DOM.setStyleAttribute(handle, "visibility", "hidden"); - - DOM.sinkEvents(getElement(), Event.MOUSEEVENTS | Event.ONMOUSEWHEEL); - DOM.sinkEvents(base, Event.ONCLICK); - DOM.sinkEvents(handle, Event.MOUSEEVENTS); - DOM.sinkEvents(smaller, Event.ONMOUSEDOWN | Event.ONMOUSEUP - | Event.ONMOUSEOUT); - DOM.sinkEvents(bigger, Event.ONMOUSEDOWN | Event.ONMOUSEUP - | Event.ONMOUSEOUT); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - - this.client = client; - id = uidl.getId(); - - // Ensure correct implementation - if (client.updateComponent(this, uidl, true)) { - return; - } - - immediate = uidl.getBooleanAttribute("immediate"); - disabled = uidl.getBooleanAttribute("disabled"); - readonly = uidl.getBooleanAttribute("readonly"); - - vertical = uidl.hasAttribute("vertical"); - arrows = uidl.hasAttribute("arrows"); - - String style = ""; - if (uidl.hasAttribute("style")) { - style = uidl.getStringAttribute("style"); - } - - scrollbarStyle = style.indexOf("scrollbar") > -1; - - if (arrows) { - DOM.setStyleAttribute(smaller, "display", "block"); - DOM.setStyleAttribute(bigger, "display", "block"); - } - - if (vertical) { - addStyleName(CLASSNAME + "-vertical"); - } else { - removeStyleName(CLASSNAME + "-vertical"); - } - - min = uidl.getDoubleAttribute("min"); - max = uidl.getDoubleAttribute("max"); - resolution = uidl.getIntAttribute("resolution"); - value = new Double(uidl.getDoubleVariable("value")); - - handleSize = uidl.getIntAttribute("hsize"); - - buildBase(); - - if (!vertical) { - // Draw handle with a delay to allow base to gain maximum width - DeferredCommand.addCommand(new Command() { - public void execute() { - buildHandle(); - setValue(value, false); - } - }); - } else { - buildHandle(); - setValue(value, false); - } - } - - private void buildBase() { - final String styleAttribute = vertical ? "height" : "width"; - final String domProperty = vertical ? "offsetHeight" : "offsetWidth"; - - if (size == -1) { - final Element p = DOM.getParent(getElement()); - if (DOM.getElementPropertyInt(p, domProperty) > 50) { - if (vertical) { - setHeight(); - } else { - DOM.setStyleAttribute(base, styleAttribute, ""); - } - } else { - // Set minimum size and adjust after all components have - // (supposedly) been drawn completely. - DOM.setStyleAttribute(base, styleAttribute, MIN_SIZE + "px"); - DeferredCommand.addCommand(new Command() { - public void execute() { - final Element p = DOM.getParent(getElement()); - if (DOM.getElementPropertyInt(p, domProperty) > (MIN_SIZE + 5)) { - if (vertical) { - setHeight(); - } else { - DOM.setStyleAttribute(base, styleAttribute, ""); - } - // Ensure correct position - setValue(value, false); - } - } - }); - } - } else { - DOM.setStyleAttribute(base, styleAttribute, size + "px"); - } - - // TODO attach listeners for focusing and arrow keys - } - - private void buildHandle() { - final String styleAttribute = vertical ? "height" : "width"; - final String handleAttribute = vertical ? "marginTop" : "marginLeft"; - final String domProperty = vertical ? "offsetHeight" : "offsetWidth"; - - DOM.setStyleAttribute(handle, handleAttribute, "0"); - - if (scrollbarStyle) { - // Only stretch the handle if scrollbar style is set. - int s = (int) (Double.parseDouble(DOM.getElementProperty(base, - domProperty)) / 100 * handleSize); - if (handleSize == -1) { - final int baseS = Integer.parseInt(DOM.getElementProperty(base, - domProperty)); - final double range = (max - min) * (resolution + 1) * 3; - s = (int) (baseS - range); - } - if (s < 3) { - s = 3; - } - DOM.setStyleAttribute(handle, styleAttribute, s + "px"); - } else { - DOM.setStyleAttribute(handle, styleAttribute, ""); - } - - // Restore visibility - DOM.setStyleAttribute(handle, "visibility", "visible"); - - } - - private void setValue(Double value, boolean updateToServer) { - if (value == null) { - return; - } - - if (value.doubleValue() < min) { - value = new Double(min); - } else if (value.doubleValue() > max) { - value = new Double(max); - } - - // Update handle position - final String styleAttribute = vertical ? "marginTop" : "marginLeft"; - final String domProperty = vertical ? "offsetHeight" : "offsetWidth"; - final int handleSize = Integer.parseInt(DOM.getElementProperty(handle, - domProperty)); - final int baseSize = Integer.parseInt(DOM.getElementProperty(base, - domProperty)) - - (2 * BASE_BORDER_WIDTH); - - final int range = baseSize - handleSize; - double v = value.doubleValue(); - // Round value to resolution - if (resolution > 0) { - v = Math.round(v * Math.pow(10, resolution)); - v = v / Math.pow(10, resolution); - } else { - v = Math.round(v); - } - final double valueRange = max - min; - double p = 0; - if (valueRange > 0) { - p = range * ((v - min) / valueRange); - } - if (p < 0) { - p = 0; - } - if (vertical) { - // IE6 rounding behaves a little unstable, reduce one pixel so the - // containing element (base) won't expand without limits - p = range - p - (BrowserInfo.get().isIE6() ? 1 : 0); - } - final double pos = p; - - DOM.setStyleAttribute(handle, styleAttribute, (Math.round(pos)) + "px"); - - // TODO give more detailed info when dragging and do roundup - DOM.setElementAttribute(handle, "title", "" + v); - - // Update value - this.value = new Double(v); - - if (updateToServer) { - updateValueToServer(); - } - } - - @Override - public void onBrowserEvent(Event event) { - if (disabled || readonly) { - return; - } - final Element targ = DOM.eventGetTarget(event); - - if (DOM.eventGetType(event) == Event.ONMOUSEWHEEL) { - processMouseWheelEvent(event); - } else if (dragging || targ == handle) { - processHandleEvent(event); - } else if (targ == smaller) { - decreaseValue(true); - } else if (targ == bigger) { - increaseValue(true); - } else { - processBaseEvent(event); - } - } - - private Timer scrollTimer; - - private void processMouseWheelEvent(final Event event) { - final int dir = DOM.eventGetMouseWheelVelocityY(event); - - if (dir < 0) { - increaseValue(false); - } else { - decreaseValue(false); - } - - if (scrollTimer != null) { - scrollTimer.cancel(); - } - scrollTimer = new Timer() { - @Override - public void run() { - updateValueToServer(); - } - }; - scrollTimer.schedule(100); - - DOM.eventPreventDefault(event); - DOM.eventCancelBubble(event, true); - } - - private void processHandleEvent(Event event) { - switch (DOM.eventGetType(event)) { - case Event.ONMOUSEDOWN: - if (!disabled && !readonly) { - dragging = true; - DOM.setCapture(getElement()); - DOM.eventPreventDefault(event); // prevent selecting text - DOM.eventCancelBubble(event, true); - } - break; - case Event.ONMOUSEMOVE: - if (dragging) { - // DOM.setCapture(getElement()); - setValueByEvent(event, false); - } - break; - case Event.ONMOUSEUP: - dragging = false; - DOM.releaseCapture(getElement()); - setValueByEvent(event, true); - break; - default: - break; - } - } - - private void processBaseEvent(Event event) { - if (DOM.eventGetType(event) == Event.ONMOUSEDOWN) { - if (!disabled && !readonly && !dragging) { - setValueByEvent(event, true); - DOM.eventCancelBubble(event, true); - } - } else if (DOM.eventGetType(event) == Event.ONMOUSEDOWN && dragging) { - dragging = false; - DOM.releaseCapture(getElement()); - setValueByEvent(event, true); - } - } - - private void decreaseValue(boolean updateToServer) { - setValue(new Double(value.doubleValue() - Math.pow(10, -resolution)), - updateToServer); - } - - private void increaseValue(boolean updateToServer) { - setValue(new Double(value.doubleValue() + Math.pow(10, -resolution)), - updateToServer); - } - - private void setValueByEvent(Event event, boolean updateToServer) { - double v = min; // Fallback to min - - final int coord = vertical ? DOM.eventGetClientY(event) : DOM - .eventGetClientX(event); - final String domProperty = vertical ? "offsetHeight" : "offsetWidth"; - - final double handleSize = Integer.parseInt(DOM.getElementProperty( - handle, domProperty)); - final double baseSize = Integer.parseInt(DOM.getElementProperty(base, - domProperty)); - final double baseOffset = vertical ? DOM.getAbsoluteTop(base) - - handleSize / 2 : DOM.getAbsoluteLeft(base) + handleSize / 2; - - if (vertical) { - v = ((baseSize - (coord - baseOffset)) / (baseSize - handleSize)) - * (max - min) + min; - } else { - v = ((coord - baseOffset) / (baseSize - handleSize)) * (max - min) - + min; - } - - if (v < min) { - v = min; - } else if (v > max) { - v = max; - } - - setValue(new Double(v), updateToServer); - } - - public void iLayout() { - if (vertical) { - setHeight(); - } - // Update handle position - setValue(value, false); - } - - private void setHeight() { - if (size == -1) { - // Calculate decoration size - DOM.setStyleAttribute(base, "height", "0"); - DOM.setStyleAttribute(base, "overflow", "hidden"); - int h = DOM.getElementPropertyInt(getElement(), "offsetHeight"); - if (h < MIN_SIZE) { - h = MIN_SIZE; - } - DOM.setStyleAttribute(base, "height", h + "px"); - } else { - DOM.setStyleAttribute(base, "height", size + "px"); - } - DOM.setStyleAttribute(base, "overflow", ""); - } - - private void updateValueToServer() { - client.updateVariable(id, "value", value.doubleValue(), immediate); - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ISplitPanel.java b/src/com/vaadin/terminal/gwt/client/ui/ISplitPanel.java deleted file mode 100644 index 74ca6b5125..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ISplitPanel.java +++ /dev/null @@ -1,584 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.Set; - -import com.google.gwt.user.client.Command; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.DeferredCommand; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.ComplexPanel; -import com.google.gwt.user.client.ui.RootPanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.Container; -import com.vaadin.terminal.gwt.client.ContainerResizedListener; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.RenderInformation; -import com.vaadin.terminal.gwt.client.RenderSpace; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; - -public class ISplitPanel extends ComplexPanel implements Container, - ContainerResizedListener { - public static final String CLASSNAME = "i-splitpanel"; - - public static final int ORIENTATION_HORIZONTAL = 0; - - public static final int ORIENTATION_VERTICAL = 1; - - private static final int MIN_SIZE = 30; - - private int orientation = ORIENTATION_HORIZONTAL; - - private Widget firstChild; - - private Widget secondChild; - - private final Element wrapper = DOM.createDiv(); - - private final Element firstContainer = DOM.createDiv(); - - private final Element secondContainer = DOM.createDiv(); - - private final Element splitter = DOM.createDiv(); - - private boolean resizing; - - private int origX; - - private int origY; - - private int origMouseX; - - private int origMouseY; - - private boolean locked = false; - - private String[] componentStyleNames; - - private Element draggingCurtain; - - private ApplicationConnection client; - - private String width = ""; - - private String height = ""; - - private RenderSpace firstRenderSpace = new RenderSpace(0, 0, true); - private RenderSpace secondRenderSpace = new RenderSpace(0, 0, true); - - RenderInformation renderInformation = new RenderInformation(); - - private String id; - - private boolean immediate; - - private boolean rendering = false; - - public ISplitPanel() { - this(ORIENTATION_HORIZONTAL); - } - - public ISplitPanel(int orientation) { - setElement(DOM.createDiv()); - switch (orientation) { - case ORIENTATION_HORIZONTAL: - setStyleName(CLASSNAME + "-horizontal"); - break; - case ORIENTATION_VERTICAL: - default: - setStyleName(CLASSNAME + "-vertical"); - break; - } - // size below will be overridden in update from uidl, initial size - // needed to keep IE alive - setWidth(MIN_SIZE + "px"); - setHeight(MIN_SIZE + "px"); - constructDom(); - setOrientation(orientation); - DOM.sinkEvents(splitter, (Event.MOUSEEVENTS)); - DOM.sinkEvents(getElement(), (Event.MOUSEEVENTS)); - } - - protected void constructDom() { - DOM.appendChild(splitter, DOM.createDiv()); // for styling - DOM.appendChild(getElement(), wrapper); - DOM.setStyleAttribute(wrapper, "position", "relative"); - DOM.setStyleAttribute(wrapper, "width", "100%"); - DOM.setStyleAttribute(wrapper, "height", "100%"); - - DOM.appendChild(wrapper, secondContainer); - DOM.appendChild(wrapper, firstContainer); - DOM.appendChild(wrapper, splitter); - - DOM.setStyleAttribute(splitter, "position", "absolute"); - DOM.setStyleAttribute(secondContainer, "position", "absolute"); - - DOM.setStyleAttribute(firstContainer, "overflow", "auto"); - DOM.setStyleAttribute(secondContainer, "overflow", "auto"); - - } - - private void setOrientation(int orientation) { - this.orientation = orientation; - if (orientation == ORIENTATION_HORIZONTAL) { - DOM.setStyleAttribute(splitter, "height", "100%"); - DOM.setStyleAttribute(splitter, "top", "0"); - DOM.setStyleAttribute(firstContainer, "height", "100%"); - DOM.setStyleAttribute(secondContainer, "height", "100%"); - } else { - DOM.setStyleAttribute(splitter, "width", "100%"); - DOM.setStyleAttribute(splitter, "left", "0"); - DOM.setStyleAttribute(firstContainer, "width", "100%"); - DOM.setStyleAttribute(secondContainer, "width", "100%"); - } - - DOM.setElementProperty(firstContainer, "className", CLASSNAME - + "-first-container"); - DOM.setElementProperty(secondContainer, "className", CLASSNAME - + "-second-container"); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - this.client = client; - id = uidl.getId(); - rendering = true; - - immediate = uidl.hasAttribute("immediate"); - - if (client.updateComponent(this, uidl, true)) { - rendering = false; - return; - } - - if (uidl.hasAttribute("style")) { - componentStyleNames = uidl.getStringAttribute("style").split(" "); - } else { - componentStyleNames = new String[0]; - } - - setLocked(uidl.getBooleanAttribute("locked")); - - setStylenames(); - - setSplitPosition(uidl.getStringAttribute("position")); - - final Paintable newFirstChild = client.getPaintable(uidl - .getChildUIDL(0)); - final Paintable newSecondChild = client.getPaintable(uidl - .getChildUIDL(1)); - if (firstChild != newFirstChild) { - if (firstChild != null) { - client.unregisterPaintable((Paintable) firstChild); - } - setFirstWidget((Widget) newFirstChild); - } - if (secondChild != newSecondChild) { - if (secondChild != null) { - client.unregisterPaintable((Paintable) secondChild); - } - setSecondWidget((Widget) newSecondChild); - } - newFirstChild.updateFromUIDL(uidl.getChildUIDL(0), client); - newSecondChild.updateFromUIDL(uidl.getChildUIDL(1), client); - - renderInformation.updateSize(getElement()); - - if (BrowserInfo.get().isIE7()) { - // Part III of IE7 hack - DeferredCommand.addCommand(new Command() { - public void execute() { - iLayout(); - } - }); - } - rendering = false; - - } - - private void setLocked(boolean newValue) { - if (locked != newValue) { - locked = newValue; - splitterSize = -1; - setStylenames(); - } - } - - private void setSplitPosition(String pos) { - if (orientation == ORIENTATION_HORIZONTAL) { - DOM.setStyleAttribute(splitter, "left", pos); - } else { - DOM.setStyleAttribute(splitter, "top", pos); - } - iLayout(); - client.runDescendentsLayout(this); - - } - - /* - * Calculates absolutely positioned container places/sizes (non-Javadoc) - * - * @see com.vaadin.terminal.gwt.client.NeedsLayout#layout() - */ - public void iLayout() { - if (!isAttached()) { - return; - } - - renderInformation.updateSize(getElement()); - - int wholeSize; - int pixelPosition; - - switch (orientation) { - case ORIENTATION_HORIZONTAL: - wholeSize = DOM.getElementPropertyInt(wrapper, "clientWidth"); - pixelPosition = DOM.getElementPropertyInt(splitter, "offsetLeft"); - - // reposition splitter in case it is out of box - if (pixelPosition > 0 - && pixelPosition + getSplitterSize() > wholeSize) { - pixelPosition = wholeSize - getSplitterSize(); - if (pixelPosition < 0) { - pixelPosition = 0; - } - setSplitPosition(pixelPosition + "px"); - return; - } - - DOM - .setStyleAttribute(firstContainer, "width", pixelPosition - + "px"); - int secondContainerWidth = (wholeSize - pixelPosition - getSplitterSize()); - if (secondContainerWidth < 0) { - secondContainerWidth = 0; - } - DOM.setStyleAttribute(secondContainer, "width", - secondContainerWidth + "px"); - DOM.setStyleAttribute(secondContainer, "left", - (pixelPosition + getSplitterSize()) + "px"); - - int contentHeight = renderInformation.getRenderedSize().getHeight(); - firstRenderSpace.setHeight(contentHeight); - firstRenderSpace.setWidth(pixelPosition); - secondRenderSpace.setHeight(contentHeight); - secondRenderSpace.setWidth(secondContainerWidth); - - break; - case ORIENTATION_VERTICAL: - wholeSize = DOM.getElementPropertyInt(wrapper, "clientHeight"); - pixelPosition = DOM.getElementPropertyInt(splitter, "offsetTop"); - - // reposition splitter in case it is out of box - if (pixelPosition > 0 - && pixelPosition + getSplitterSize() > wholeSize) { - pixelPosition = wholeSize - getSplitterSize(); - if (pixelPosition < 0) { - pixelPosition = 0; - } - setSplitPosition(pixelPosition + "px"); - return; - } - - DOM.setStyleAttribute(firstContainer, "height", pixelPosition - + "px"); - int secondContainerHeight = (wholeSize - pixelPosition - getSplitterSize()); - if (secondContainerHeight < 0) { - secondContainerHeight = 0; - } - DOM.setStyleAttribute(secondContainer, "height", - secondContainerHeight + "px"); - DOM.setStyleAttribute(secondContainer, "top", - (pixelPosition + getSplitterSize()) + "px"); - - int contentWidth = renderInformation.getRenderedSize().getWidth(); - firstRenderSpace.setHeight(pixelPosition); - firstRenderSpace.setWidth(contentWidth); - secondRenderSpace.setHeight(secondContainerHeight); - secondRenderSpace.setWidth(contentWidth); - - break; - } - - // fixes scrollbars issues on webkit based browsers - Util.runWebkitOverflowAutoFix(secondContainer); - Util.runWebkitOverflowAutoFix(firstContainer); - - } - - private void setFirstWidget(Widget w) { - if (firstChild != null) { - firstChild.removeFromParent(); - } - super.add(w, firstContainer); - firstChild = w; - } - - private void setSecondWidget(Widget w) { - if (secondChild != null) { - secondChild.removeFromParent(); - } - super.add(w, secondContainer); - secondChild = w; - } - - @Override - public void onBrowserEvent(Event event) { - switch (DOM.eventGetType(event)) { - case Event.ONMOUSEMOVE: - if (resizing) { - onMouseMove(event); - } - break; - case Event.ONMOUSEDOWN: - onMouseDown(event); - break; - case Event.ONMOUSEUP: - if (resizing) { - onMouseUp(event); - } - break; - case Event.ONCLICK: - resizing = false; - break; - } - } - - public void onMouseDown(Event event) { - if (locked) { - return; - } - final Element trg = DOM.eventGetTarget(event); - if (trg == splitter || trg == DOM.getChild(splitter, 0)) { - resizing = true; - if (BrowserInfo.get().isGecko()) { - showDraggingCurtain(); - } - DOM.setCapture(getElement()); - origX = DOM.getElementPropertyInt(splitter, "offsetLeft"); - origY = DOM.getElementPropertyInt(splitter, "offsetTop"); - origMouseX = DOM.eventGetClientX(event); - origMouseY = DOM.eventGetClientY(event); - DOM.eventCancelBubble(event, true); - DOM.eventPreventDefault(event); - } - } - - public void onMouseMove(Event event) { - switch (orientation) { - case ORIENTATION_HORIZONTAL: - final int x = DOM.eventGetClientX(event); - onHorizontalMouseMove(x); - break; - case ORIENTATION_VERTICAL: - default: - final int y = DOM.eventGetClientY(event); - onVerticalMouseMove(y); - break; - } - iLayout(); - // TODO Check if this is needed - client.runDescendentsLayout(this); - - } - - private void onHorizontalMouseMove(int x) { - int newX = origX + x - origMouseX; - if (newX < 0) { - newX = 0; - } - if (newX + getSplitterSize() > getOffsetWidth()) { - newX = getOffsetWidth() - getSplitterSize(); - } - DOM.setStyleAttribute(splitter, "left", newX + "px"); - updateSplitPosition(newX); - } - - private void onVerticalMouseMove(int y) { - int newY = origY + y - origMouseY; - if (newY < 0) { - newY = 0; - } - - if (newY + getSplitterSize() > getOffsetHeight()) { - newY = getOffsetHeight() - getSplitterSize(); - } - DOM.setStyleAttribute(splitter, "top", newY + "px"); - updateSplitPosition(newY); - } - - public void onMouseUp(Event event) { - DOM.releaseCapture(getElement()); - if (BrowserInfo.get().isGecko()) { - hideDraggingCurtain(); - } - resizing = false; - onMouseMove(event); - } - - /** - * Used in FF to avoid losing mouse capture when pointer is moved on an - * iframe. - */ - private void showDraggingCurtain() { - if (draggingCurtain == null) { - draggingCurtain = DOM.createDiv(); - DOM.setStyleAttribute(draggingCurtain, "position", "absolute"); - DOM.setStyleAttribute(draggingCurtain, "top", "0px"); - DOM.setStyleAttribute(draggingCurtain, "left", "0px"); - DOM.setStyleAttribute(draggingCurtain, "width", "100%"); - DOM.setStyleAttribute(draggingCurtain, "height", "100%"); - DOM.setStyleAttribute(draggingCurtain, "zIndex", "" - + IToolkitOverlay.Z_INDEX); - DOM.appendChild(RootPanel.getBodyElement(), draggingCurtain); - } - } - - /** - * Hides dragging curtain - */ - private void hideDraggingCurtain() { - if (draggingCurtain != null) { - DOM.removeChild(RootPanel.getBodyElement(), draggingCurtain); - draggingCurtain = null; - } - } - - private int splitterSize = -1; - - private int getSplitterSize() { - if (splitterSize < 0) { - if (isAttached()) { - switch (orientation) { - case ORIENTATION_HORIZONTAL: - splitterSize = DOM.getElementPropertyInt(splitter, - "offsetWidth"); - break; - - default: - splitterSize = DOM.getElementPropertyInt(splitter, - "offsetHeight"); - break; - } - } - } - return splitterSize; - } - - @Override - public void setHeight(String height) { - if (this.height.equals(height)) { - return; - } - - this.height = height; - super.setHeight(height); - if (!rendering && client != null) { - iLayout(); - client.runDescendentsLayout(this); - } - } - - @Override - public void setWidth(String width) { - if (this.width.equals(width)) { - return; - } - - this.width = width; - super.setWidth(width); - if (!rendering && client != null) { - iLayout(); - client.runDescendentsLayout(this); - } - } - - public RenderSpace getAllocatedSpace(Widget child) { - if (child == firstChild) { - return firstRenderSpace; - } else if (child == secondChild) { - return secondRenderSpace; - } - - return null; - } - - public boolean hasChildComponent(Widget component) { - return (component != null && (component == firstChild || component == secondChild)); - } - - public void replaceChildComponent(Widget oldComponent, Widget newComponent) { - if (oldComponent == firstChild) { - setFirstWidget(newComponent); - } else if (oldComponent == secondChild) { - setSecondWidget(newComponent); - } - } - - public boolean requestLayout(Set child) { - if (height != null && width != null) { - /* - * If the height and width has been specified the child components - * cannot make the size of the layout change - */ - - return true; - } - - if (renderInformation.updateSize(getElement())) { - return false; - } else { - return true; - } - - } - - public void updateCaption(Paintable component, UIDL uidl) { - // TODO Implement caption handling - } - - /** - * Updates the new split position back to server. - * - * @param pos - * The new position of the split handle. - */ - private void updateSplitPosition(int pos) { - // We always send pixel values to server - client.updateVariable(id, "position", pos, immediate); - } - - private void setStylenames() { - final String splitterSuffix = (orientation == ORIENTATION_HORIZONTAL ? "-hsplitter" - : "-vsplitter"); - final String firstContainerSuffix = "-first-container"; - final String secondContainerSuffix = "-second-container"; - String lockedSuffix = ""; - - String splitterStyle = CLASSNAME + splitterSuffix; - String firstStyle = CLASSNAME + firstContainerSuffix; - String secondStyle = CLASSNAME + secondContainerSuffix; - - if (locked) { - splitterStyle = CLASSNAME + splitterSuffix + "-locked"; - lockedSuffix = "-locked"; - } - for (int i = 0; i < componentStyleNames.length; i++) { - splitterStyle += " " + CLASSNAME + splitterSuffix + "-" - + componentStyleNames[i] + lockedSuffix; - firstStyle += " " + CLASSNAME + firstContainerSuffix + "-" - + componentStyleNames[i]; - secondStyle += " " + CLASSNAME + secondContainerSuffix + "-" - + componentStyleNames[i]; - } - DOM.setElementProperty(splitter, "className", splitterStyle); - DOM.setElementProperty(firstContainer, "className", firstStyle); - DOM.setElementProperty(secondContainer, "className", secondStyle); - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ISplitPanelHorizontal.java b/src/com/vaadin/terminal/gwt/client/ui/ISplitPanelHorizontal.java deleted file mode 100644 index 140881484b..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ISplitPanelHorizontal.java +++ /dev/null @@ -1,12 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -public class ISplitPanelHorizontal extends ISplitPanel { - - public ISplitPanelHorizontal() { - super(ISplitPanel.ORIENTATION_HORIZONTAL); - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ISplitPanelVertical.java b/src/com/vaadin/terminal/gwt/client/ui/ISplitPanelVertical.java deleted file mode 100644 index eff00e8a43..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ISplitPanelVertical.java +++ /dev/null @@ -1,12 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -public class ISplitPanelVertical extends ISplitPanel { - - public ISplitPanelVertical() { - super(ISplitPanel.ORIENTATION_VERTICAL); - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ITablePaging.java b/src/com/vaadin/terminal/gwt/client/ui/ITablePaging.java deleted file mode 100644 index 224c549f3c..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ITablePaging.java +++ /dev/null @@ -1,439 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Set; -import java.util.Vector; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.ui.Button; -import com.google.gwt.user.client.ui.ClickListener; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.Grid; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.HorizontalPanel; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.SimplePanel; -import com.google.gwt.user.client.ui.VerticalPanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; - -/** - * TODO make this work (just an early prototype). We may want to have paging - * style table which will be much lighter than IScrollTable is. - */ -public class ITablePaging extends Composite implements Table, Paintable, - ClickListener { - - private final Grid tBody = new Grid(); - private final Button nextPage = new Button(">"); - private final Button prevPage = new Button("<"); - private final Button firstPage = new Button("<<"); - private final Button lastPage = new Button(">>"); - - private int pageLength = 15; - - private boolean rowHeaders = false; - - private ApplicationConnection client; - private String id; - - private boolean immediate = false; - - private int selectMode = Table.SELECT_MODE_NONE; - - private final Vector selectedRowKeys = new Vector(); - - private int totalRows; - - private final HashMap visibleColumns = new HashMap(); - - private int rows; - - private int firstRow; - private boolean sortAscending = true; - private final HorizontalPanel pager; - - public HashMap rowKeysToTableRows = new HashMap(); - - public ITablePaging() { - - tBody.setStyleName("itable-tbody"); - - final VerticalPanel panel = new VerticalPanel(); - - pager = new HorizontalPanel(); - pager.add(firstPage); - firstPage.addClickListener(this); - pager.add(prevPage); - prevPage.addClickListener(this); - pager.add(nextPage); - nextPage.addClickListener(this); - pager.add(lastPage); - lastPage.addClickListener(this); - - panel.add(pager); - panel.add(tBody); - - initWidget(panel); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - if (client.updateComponent(this, uidl, true)) { - return; - } - - this.client = client; - id = uidl.getStringAttribute("id"); - immediate = uidl.getBooleanAttribute("immediate"); - totalRows = uidl.getIntAttribute("totalrows"); - pageLength = uidl.getIntAttribute("pagelength"); - firstRow = uidl.getIntAttribute("firstrow"); - rows = uidl.getIntAttribute("rows"); - - if (uidl.hasAttribute("selectmode")) { - if (uidl.getStringAttribute("selectmode").equals("multi")) { - selectMode = Table.SELECT_MODE_MULTI; - } else { - selectMode = Table.SELECT_MODE_SINGLE; - } - - if (uidl.hasAttribute("selected")) { - final Set selectedKeys = uidl - .getStringArrayVariableAsSet("selected"); - selectedRowKeys.clear(); - for (final Iterator it = selectedKeys.iterator(); it.hasNext();) { - selectedRowKeys.add(it.next()); - } - } - } - - if (uidl.hasVariable("sortascending")) { - sortAscending = uidl.getBooleanVariable("sortascending"); - } - - if (uidl.hasAttribute("rowheaders")) { - rowHeaders = true; - } - - UIDL rowData = null; - UIDL visibleColumns = null; - for (final Iterator it = uidl.getChildIterator(); it.hasNext();) { - final UIDL c = (UIDL) it.next(); - if (c.getTag().equals("rows")) { - rowData = c; - } else if (c.getTag().equals("actions")) { - updateActionMap(c); - } else if (c.getTag().equals("visiblecolumns")) { - visibleColumns = c; - } - } - tBody.resize(rows + 1, uidl.getIntAttribute("cols") - + (rowHeaders ? 1 : 0)); - updateHeader(visibleColumns); - updateBody(rowData); - - updatePager(); - } - - private void updateHeader(UIDL c) { - final Iterator it = c.getChildIterator(); - visibleColumns.clear(); - int colIndex = (rowHeaders ? 1 : 0); - while (it.hasNext()) { - final UIDL col = (UIDL) it.next(); - final String cid = col.getStringAttribute("cid"); - if (!col.hasAttribute("collapsed")) { - tBody.setWidget(0, colIndex, new HeaderCell(cid, col - .getStringAttribute("caption"))); - - } - colIndex++; - } - } - - private void updateActionMap(UIDL c) { - // TODO Auto-generated method stub - - } - - /** - * Updates row data from uidl. UpdateFromUIDL delegates updating tBody to - * this method. - * - * Updates may be to different part of tBody, depending on update type. It - * can be initial row data, scroll up, scroll down... - * - * @param uidl - * which contains row data - */ - private void updateBody(UIDL uidl) { - final Iterator it = uidl.getChildIterator(); - - int curRowIndex = 1; - while (it.hasNext()) { - final UIDL rowUidl = (UIDL) it.next(); - final TableRow row = new TableRow(curRowIndex, String - .valueOf(rowUidl.getIntAttribute("key")), rowUidl - .hasAttribute("selected")); - int colIndex = 0; - if (rowHeaders) { - tBody.setWidget(curRowIndex, colIndex, new BodyCell(row, - rowUidl.getStringAttribute("caption"))); - colIndex++; - } - final Iterator cells = rowUidl.getChildIterator(); - while (cells.hasNext()) { - final Object cell = cells.next(); - if (cell instanceof String) { - tBody.setWidget(curRowIndex, colIndex, new BodyCell(row, - (String) cell)); - } else { - final Paintable cellContent = client - .getPaintable((UIDL) cell); - final BodyCell bodyCell = new BodyCell(row); - bodyCell.setWidget((Widget) cellContent); - tBody.setWidget(curRowIndex, colIndex, bodyCell); - } - colIndex++; - } - curRowIndex++; - } - } - - private void updatePager() { - if (pageLength == 0) { - pager.setVisible(false); - return; - } - if (isFirstPage()) { - firstPage.setEnabled(false); - prevPage.setEnabled(false); - } else { - firstPage.setEnabled(true); - prevPage.setEnabled(true); - } - if (hasNextPage()) { - nextPage.setEnabled(true); - lastPage.setEnabled(true); - } else { - nextPage.setEnabled(false); - lastPage.setEnabled(false); - - } - } - - private boolean hasNextPage() { - if (firstRow + rows + 1 > totalRows) { - return false; - } - return true; - } - - private boolean isFirstPage() { - if (firstRow == 0) { - return true; - } - return false; - } - - public void onClick(Widget sender) { - if (sender instanceof Button) { - if (sender == firstPage) { - client.updateVariable(id, "firstvisible", 0, true); - } else if (sender == nextPage) { - client.updateVariable(id, "firstvisible", - firstRow + pageLength, true); - } else if (sender == prevPage) { - int newFirst = firstRow - pageLength; - if (newFirst < 0) { - newFirst = 0; - } - client.updateVariable(id, "firstvisible", newFirst, true); - } else if (sender == lastPage) { - client.updateVariable(id, "firstvisible", totalRows - - pageLength, true); - } - } - if (sender instanceof HeaderCell) { - final HeaderCell hCell = (HeaderCell) sender; - client.updateVariable(id, "sortcolumn", hCell.getCid(), false); - client.updateVariable(id, "sortascending", (sortAscending ? false - : true), true); - } - } - - private class HeaderCell extends HTML { - - private String cid; - - public String getCid() { - return cid; - } - - public void setCid(String pid) { - cid = pid; - } - - HeaderCell(String pid, String caption) { - super(); - cid = pid; - addClickListener(ITablePaging.this); - setText(caption); - // TODO remove debug color - DOM.setStyleAttribute(getElement(), "color", "brown"); - DOM.setStyleAttribute(getElement(), "font-weight", "bold"); - } - } - - /** - * Abstraction of table cell content. In needs to know on which row it is in - * case of context click. - * - * @author mattitahvonen - */ - public class BodyCell extends SimplePanel { - private final TableRow row; - - public BodyCell(TableRow row) { - super(); - sinkEvents(Event.BUTTON_LEFT | Event.BUTTON_RIGHT); - this.row = row; - } - - public BodyCell(TableRow row2, String textContent) { - super(); - sinkEvents(Event.BUTTON_LEFT | Event.BUTTON_RIGHT); - row = row2; - setWidget(new Label(textContent)); - } - - @Override - public void onBrowserEvent(Event event) { - System.out.println("CEll event: " + event.toString()); - switch (DOM.eventGetType(event)) { - case Event.BUTTON_RIGHT: - row.showContextMenu(event); - Window.alert("context menu un-implemented"); - DOM.eventCancelBubble(event, true); - break; - case Event.BUTTON_LEFT: - if (selectMode > Table.SELECT_MODE_NONE) { - row.toggleSelected(); - } - break; - default: - break; - } - super.onBrowserEvent(event); - } - } - - private class TableRow { - - private final String key; - private final int rowIndex; - private boolean selected = false; - - public TableRow(int rowIndex, String rowKey, boolean selected) { - rowKeysToTableRows.put(rowKey, this); - this.rowIndex = rowIndex; - key = rowKey; - setSelected(selected); - } - - /** - * This method is used to set row status. Does not change value on - * server. - * - * @param selected - */ - public void setSelected(boolean sel) { - selected = sel; - if (selected) { - selectedRowKeys.add(key); - DOM.setStyleAttribute(tBody.getRowFormatter().getElement( - rowIndex), "background", "yellow"); - - } else { - selectedRowKeys.remove(key); - DOM.setStyleAttribute(tBody.getRowFormatter().getElement( - rowIndex), "background", "transparent"); - } - } - - public void setContextMenuOptions(HashMap options) { - - } - - /** - * Toggles rows select state. Also updates state to server according to - * tables immediate flag. - * - */ - public void toggleSelected() { - if (selected) { - setSelected(false); - } else { - if (selectMode == Table.SELECT_MODE_SINGLE) { - deselectAll(); - } - setSelected(true); - } - client.updateVariable(id, "selected", selectedRowKeys.toArray(), - immediate); - } - - /** - * Shows context menu for this row. - * - * @param event - * Event which triggered context menu. Correct place for - * context menu can be determined with it. - */ - public void showContextMenu(Event event) { - System.out.println("TODO: Show context menu"); - } - } - - public void deselectAll() { - final Object[] keys = selectedRowKeys.toArray(); - for (int i = 0; i < keys.length; i++) { - final TableRow tableRow = (TableRow) rowKeysToTableRows - .get(keys[i]); - if (tableRow != null) { - tableRow.setSelected(false); - } - } - // still ensure all selects are removed from - selectedRowKeys.clear(); - } - - public void add(Widget w) { - // TODO Auto-generated method stub - - } - - public void clear() { - // TODO Auto-generated method stub - - } - - public Iterator iterator() { - // TODO Auto-generated method stub - return null; - } - - public boolean remove(Widget w) { - // TODO Auto-generated method stub - return false; - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ITabsheet.java b/src/com/vaadin/terminal/gwt/client/ui/ITabsheet.java deleted file mode 100644 index 52f5a41062..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ITabsheet.java +++ /dev/null @@ -1,841 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Set; - -import com.google.gwt.dom.client.Style; -import com.google.gwt.user.client.Command; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.DeferredCommand; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.ClickListener; -import com.google.gwt.user.client.ui.ComplexPanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.ICaption; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.RenderInformation; -import com.vaadin.terminal.gwt.client.RenderSpace; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; - -public class ITabsheet extends ITabsheetBase { - - private class TabSheetCaption extends ICaption { - TabSheetCaption() { - super(null, client); - } - - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - if (event.getTypeInt() == Event.ONLOAD) { - // icon onloads may change total width of tabsheet - if (isDynamicWidth()) { - updateDynamicWidth(); - } - updateTabScroller(); - } - } - - @Override - public void setWidth(String width) { - super.setWidth(width); - if (BrowserInfo.get().isIE7()) { - /* - * IE7 apparently has problems with calculating width for - * floated elements inside a DIV with padding. Set the width - * explicitly for the caption. - */ - fixTextWidth(); - } - } - - private void fixTextWidth() { - Element captionText = getTextElement(); - int captionWidth = Util.getRequiredWidth(captionText); - int scrollWidth = captionText.getScrollWidth(); - if (scrollWidth > captionWidth) { - captionWidth = scrollWidth; - } - captionText.getStyle().setPropertyPx("width", captionWidth); - } - - } - - class TabBar extends ComplexPanel implements ClickListener { - - private Element tr = DOM.createTR(); - - private Element spacerTd = DOM.createTD(); - - TabBar() { - Element el = DOM.createTable(); - Element tbody = DOM.createTBody(); - DOM.appendChild(el, tbody); - DOM.appendChild(tbody, tr); - setStyleName(spacerTd, CLASSNAME + "-spacertd"); - DOM.appendChild(tr, spacerTd); - DOM.appendChild(spacerTd, DOM.createDiv()); - setElement(el); - } - - protected Element getContainerElement() { - return tr; - } - - private Widget oldSelected; - - public int getTabCount() { - return getWidgetCount(); - } - - public void addTab(ICaption c) { - Element td = DOM.createTD(); - setStyleName(td, CLASSNAME + "-tabitemcell"); - - if (getWidgetCount() == 0) { - setStyleName(td, CLASSNAME + "-tabitemcell-first", true); - } - - Element div = DOM.createDiv(); - setStyleName(div, CLASSNAME + "-tabitem"); - DOM.appendChild(td, div); - DOM.insertBefore(tr, td, spacerTd); - c.addClickListener(this); - add(c, div); - } - - public void onClick(Widget sender) { - int index = getWidgetIndex(sender); - onTabSelected(index); - } - - public void selectTab(int index) { - Widget newSelected = getWidget(index); - Widget.setStyleName(DOM.getParent(newSelected.getElement()), - CLASSNAME + "-tabitem-selected", true); - if (oldSelected != null && oldSelected != newSelected) { - Widget.setStyleName(DOM.getParent(oldSelected.getElement()), - CLASSNAME + "-tabitem-selected", false); - } - oldSelected = newSelected; - } - - public void removeTab(int i) { - Widget w = getWidget(i); - if (w == null) { - return; - } - - Element caption = w.getElement(); - Element div = DOM.getParent(caption); - Element td = DOM.getParent(div); - Element tr = DOM.getParent(td); - remove(w); - - /* - * Widget is the Caption but we want to remove everything up to and - * including the parent TD - */ - - DOM.removeChild(tr, td); - - /* - * If this widget was selected we need to unmark it as the last - * selected - */ - if (w == oldSelected) { - oldSelected = null; - } - } - - @Override - public boolean remove(Widget w) { - ((ICaption) w).removeClickListener(this); - return super.remove(w); - } - - public TabSheetCaption getTab(int index) { - if (index >= getWidgetCount()) { - return null; - } - return (TabSheetCaption) getWidget(index); - } - - public void setVisible(int index, boolean visible) { - Element e = DOM.getParent(getTab(index).getElement()); - if (visible) { - DOM.setStyleAttribute(e, "display", ""); - } else { - DOM.setStyleAttribute(e, "display", "none"); - } - } - - public void updateCaptionSize(int index) { - ICaption c = getTab(index); - c.setWidth(c.getRequiredWidth() + "px"); - - } - - } - - public static final String CLASSNAME = "i-tabsheet"; - - public static final String TABS_CLASSNAME = "i-tabsheet-tabcontainer"; - public static final String SCROLLER_CLASSNAME = "i-tabsheet-scroller"; - private final Element tabs; // tabbar and 'scroller' container - private final Element scroller; // tab-scroller element - private final Element scrollerNext; // tab-scroller next button element - private final Element scrollerPrev; // tab-scroller prev button element - private int scrollerIndex = 0; - - private final TabBar tb = new TabBar(); - private final ITabsheetPanel tp = new ITabsheetPanel(); - private final Element contentNode, deco; - - private final HashMap captions = new HashMap(); - - private String height; - private String width; - - private boolean waitingForResponse; - - private RenderInformation renderInformation = new RenderInformation(); - - /** - * Previous visible widget is set invisible with CSS (not display: none, but - * visibility: hidden), to avoid flickering during render process. Normal - * visibility must be returned later when new widget is rendered. - */ - private Widget previousVisibleWidget; - - private boolean rendering = false; - - private void onTabSelected(final int tabIndex) { - if (disabled || waitingForResponse) { - return; - } - final Object tabKey = tabKeys.get(tabIndex); - if (disabledTabKeys.contains(tabKey)) { - return; - } - if (client != null && activeTabIndex != tabIndex) { - tb.selectTab(tabIndex); - addStyleDependentName("loading"); - // run updating variables in deferred command to bypass some FF - // optimization issues - DeferredCommand.addCommand(new Command() { - public void execute() { - previousVisibleWidget = tp.getWidget(tp.getVisibleWidget()); - DOM.setStyleAttribute(DOM.getParent(previousVisibleWidget - .getElement()), "visibility", "hidden"); - client.updateVariable(id, "selected", tabKeys.get(tabIndex) - .toString(), true); - } - }); - waitingForResponse = true; - } - } - - private boolean isDynamicWidth() { - return width == null || width.equals(""); - } - - private boolean isDynamicHeight() { - return height == null || height.equals(""); - } - - public ITabsheet() { - super(CLASSNAME); - - // Tab scrolling - DOM.setStyleAttribute(getElement(), "overflow", "hidden"); - tabs = DOM.createDiv(); - DOM.setElementProperty(tabs, "className", TABS_CLASSNAME); - scroller = DOM.createDiv(); - - DOM.setElementProperty(scroller, "className", SCROLLER_CLASSNAME); - scrollerPrev = DOM.createButton(); - DOM.setElementProperty(scrollerPrev, "className", SCROLLER_CLASSNAME - + "Prev"); - DOM.sinkEvents(scrollerPrev, Event.ONCLICK); - scrollerNext = DOM.createButton(); - DOM.setElementProperty(scrollerNext, "className", SCROLLER_CLASSNAME - + "Next"); - DOM.sinkEvents(scrollerNext, Event.ONCLICK); - DOM.appendChild(getElement(), tabs); - - // Tabs - tp.setStyleName(CLASSNAME + "-tabsheetpanel"); - contentNode = DOM.createDiv(); - - deco = DOM.createDiv(); - - addStyleDependentName("loading"); // Indicate initial progress - tb.setStyleName(CLASSNAME + "-tabs"); - DOM - .setElementProperty(contentNode, "className", CLASSNAME - + "-content"); - DOM.setElementProperty(deco, "className", CLASSNAME + "-deco"); - - add(tb, tabs); - DOM.appendChild(scroller, scrollerPrev); - DOM.appendChild(scroller, scrollerNext); - - DOM.appendChild(getElement(), contentNode); - add(tp, contentNode); - DOM.appendChild(getElement(), deco); - - DOM.appendChild(tabs, scroller); - - // TODO Use for Safari only. Fix annoying 1px first cell in TabBar. - // DOM.setStyleAttribute(DOM.getFirstChild(DOM.getFirstChild(DOM - // .getFirstChild(tb.getElement()))), "display", "none"); - - } - - @Override - public void onBrowserEvent(Event event) { - - // Tab scrolling - if (isScrolledTabs() && DOM.eventGetTarget(event) == scrollerPrev) { - if (scrollerIndex > 0) { - scrollerIndex--; - DOM.setStyleAttribute(DOM.getChild(DOM.getFirstChild(DOM - .getFirstChild(tb.getElement())), scrollerIndex), - "display", ""); - tb.updateCaptionSize(scrollerIndex); - updateTabScroller(); - } - } else if (isClippedTabs() && DOM.eventGetTarget(event) == scrollerNext) { - int tabs = tb.getTabCount(); - if (scrollerIndex + 1 <= tabs) { - DOM.setStyleAttribute(DOM.getChild(DOM.getFirstChild(DOM - .getFirstChild(tb.getElement())), scrollerIndex), - "display", "none"); - tb.updateCaptionSize(scrollerIndex); - scrollerIndex++; - updateTabScroller(); - } - } else { - super.onBrowserEvent(event); - } - } - - @Override - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - rendering = true; - - super.updateFromUIDL(uidl, client); - if (cachedUpdate) { - return; - } - - // Add proper stylenames for all elements (easier to prevent unwanted - // style inheritance) - if (uidl.hasAttribute("style")) { - final String[] styles = uidl.getStringAttribute("style").split(" "); - final String contentBaseClass = CLASSNAME + "-content"; - String contentClass = contentBaseClass; - final String decoBaseClass = CLASSNAME + "-deco"; - String decoClass = decoBaseClass; - for (int i = 0; i < styles.length; i++) { - tb.addStyleDependentName(styles[i]); - contentClass += " " + contentBaseClass + "-" + styles[i]; - decoClass += " " + decoBaseClass + "-" + styles[i]; - } - DOM.setElementProperty(contentNode, "className", contentClass); - DOM.setElementProperty(deco, "className", decoClass); - } else { - tb.setStyleName(CLASSNAME + "-tabs"); - DOM.setElementProperty(contentNode, "className", CLASSNAME - + "-content"); - DOM.setElementProperty(deco, "className", CLASSNAME + "-deco"); - } - - if (uidl.hasAttribute("hidetabs")) { - tb.setVisible(false); - addStyleName(CLASSNAME + "-hidetabs"); - } else { - tb.setVisible(true); - removeStyleName(CLASSNAME + "-hidetabs"); - } - - // tabs; push or not - if (!isDynamicWidth()) { - // FIXME: This makes tab sheet tabs go to 1px width on every update - // and then back to original width - // update width later, in updateTabScroller(); - DOM.setStyleAttribute(tabs, "width", "1px"); - DOM.setStyleAttribute(tabs, "overflow", "hidden"); - } else { - showAllTabs(); - DOM.setStyleAttribute(tabs, "width", ""); - DOM.setStyleAttribute(tabs, "overflow", "visible"); - updateDynamicWidth(); - } - - if (!isDynamicHeight()) { - // Must update height after the styles have been set - updateContentNodeHeight(); - updateOpenTabSize(); - } - - iLayout(); - - // Re run relative size update to ensure optimal scrollbars - // TODO isolate to situation that visible tab has undefined height - try { - client.handleComponentRelativeSize(tp.getWidget(tp - .getVisibleWidget())); - } catch (Exception e) { - // Ignore, most likely empty tabsheet - } - - renderInformation.updateSize(getElement()); - - waitingForResponse = false; - rendering = false; - } - - private void updateDynamicWidth() { - // Find tab width - int tabsWidth = 0; - - int count = tb.getTabCount(); - for (int i = 0; i < count; i++) { - Element tabTd = tb.getTab(i).getElement().getParentElement().cast(); - tabsWidth += tabTd.getOffsetWidth(); - } - - // Find content width - Style style = tp.getElement().getStyle(); - String overflow = style.getProperty("overflow"); - style.setProperty("overflow", "hidden"); - style.setPropertyPx("width", tabsWidth); - Style wrapperstyle = tp.getWidget(tp.getVisibleWidget()).getElement() - .getParentElement().getStyle(); - wrapperstyle.setPropertyPx("width", tabsWidth); - // Get content width from actual widget - - int contentWidth = 0; - if (tp.getWidgetCount() > 0) { - contentWidth = tp.getWidget(tp.getVisibleWidget()).getOffsetWidth(); - } - style.setProperty("overflow", overflow); - - // Set widths to max(tabs,content) - if (tabsWidth < contentWidth) { - tabsWidth = contentWidth; - } - - int outerWidth = tabsWidth + getContentAreaBorderWidth(); - - tabs.getStyle().setPropertyPx("width", outerWidth); - style.setPropertyPx("width", tabsWidth); - wrapperstyle.setPropertyPx("width", tabsWidth); - - contentNode.getStyle().setPropertyPx("width", tabsWidth); - super.setWidth(outerWidth + "px"); - updateOpenTabSize(); - } - - @Override - protected void renderTab(final UIDL tabUidl, int index, boolean selected, - boolean hidden) { - TabSheetCaption c = tb.getTab(index); - if (c == null) { - c = new TabSheetCaption(); - tb.addTab(c); - } - c.updateCaption(tabUidl); - - tb.setVisible(index, !hidden); - - /* - * Force the width of the caption container so the content will not wrap - * and tabs won't be too narrow in certain browsers - */ - c.setWidth(c.getRequiredWidth() + "px"); - captions.put("" + index, c); - - UIDL tabContentUIDL = null; - Paintable tabContent = null; - if (tabUidl.getChildCount() > 0) { - tabContentUIDL = tabUidl.getChildUIDL(0); - tabContent = client.getPaintable(tabContentUIDL); - } - - if (tabContent != null) { - /* This is a tab with content information */ - - int oldIndex = tp.getWidgetIndex((Widget) tabContent); - if (oldIndex != -1 && oldIndex != index) { - /* - * The tab has previously been rendered in another position so - * we must move the cached content to correct position - */ - tp.insert((Widget) tabContent, index); - } - } else { - /* A tab whose content has not yet been loaded */ - - /* - * Make sure there is a corresponding empty tab in tp. The same - * operation as the moving above but for not-loaded tabs. - */ - if (index < tp.getWidgetCount()) { - Widget oldWidget = tp.getWidget(index); - if (!(oldWidget instanceof PlaceHolder)) { - tp.insert(new PlaceHolder(), index); - } - } - - } - - if (selected) { - renderContent(tabContentUIDL); - tb.selectTab(index); - } else { - if (tabContentUIDL != null) { - // updating a drawn child on hidden tab - if (tp.getWidgetIndex((Widget) tabContent) < 0) { - tp.insert((Widget) tabContent, index); - } - tabContent.updateFromUIDL(tabContentUIDL, client); - } else if (tp.getWidgetCount() <= index) { - tp.add(new PlaceHolder()); - } - } - } - - public class PlaceHolder extends ILabel { - public PlaceHolder() { - super(""); - } - } - - @Override - protected void selectTab(int index, final UIDL contentUidl) { - if (index != activeTabIndex) { - activeTabIndex = index; - tb.selectTab(activeTabIndex); - } - renderContent(contentUidl); - } - - private void renderContent(final UIDL contentUIDL) { - final Paintable content = client.getPaintable(contentUIDL); - if (tp.getWidgetCount() > activeTabIndex) { - Widget old = tp.getWidget(activeTabIndex); - if (old != content) { - tp.remove(activeTabIndex); - if (old instanceof Paintable) { - client.unregisterPaintable((Paintable) old); - } - tp.insert((Widget) content, activeTabIndex); - } - } else { - tp.add((Widget) content); - } - - tp.showWidget(activeTabIndex); - - ITabsheet.this.iLayout(); - (content).updateFromUIDL(contentUIDL, client); - /* - * The size of a cached, relative sized component must be updated to - * report correct size to updateOpenTabSize(). - */ - if (contentUIDL.getBooleanAttribute("cached")) { - client.handleComponentRelativeSize((Widget) content); - } - updateOpenTabSize(); - ITabsheet.this.removeStyleDependentName("loading"); - if (previousVisibleWidget != null) { - DOM.setStyleAttribute(previousVisibleWidget.getElement(), - "visibility", ""); - previousVisibleWidget = null; - } - } - - @Override - public void setHeight(String height) { - super.setHeight(height); - this.height = height; - updateContentNodeHeight(); - - if (!rendering) { - updateOpenTabSize(); - iLayout(); - // TODO Check if this is needed - client.runDescendentsLayout(this); - } - } - - private void updateContentNodeHeight() { - if (height != null && !"".equals(height)) { - int contentHeight = getOffsetHeight(); - contentHeight -= DOM.getElementPropertyInt(deco, "offsetHeight"); - contentHeight -= tb.getOffsetHeight(); - if (contentHeight < 0) { - contentHeight = 0; - } - - // Set proper values for content element - DOM.setStyleAttribute(contentNode, "height", contentHeight + "px"); - renderSpace.setHeight(contentHeight); - } else { - DOM.setStyleAttribute(contentNode, "height", ""); - renderSpace.setHeight(0); - } - } - - @Override - public void setWidth(String width) { - if ((this.width == null && width.equals("")) - || (this.width != null && this.width.equals(width))) { - return; - } - - super.setWidth(width); - if (width.equals("")) { - width = null; - } - this.width = width; - if (width == null) { - renderSpace.setWidth(0); - contentNode.getStyle().setProperty("width", ""); - } else { - int contentWidth = getOffsetWidth() - getContentAreaBorderWidth(); - if (contentWidth < 0) { - contentWidth = 0; - } - contentNode.getStyle().setProperty("width", contentWidth + "px"); - renderSpace.setWidth(contentWidth); - } - - if (!rendering) { - if (isDynamicHeight()) { - Util.updateRelativeChildrenAndSendSizeUpdateEvent(client, tp, - this); - } - - updateOpenTabSize(); - iLayout(); - // TODO Check if this is needed - client.runDescendentsLayout(this); - - } - - } - - public void iLayout() { - updateTabScroller(); - tp.runWebkitOverflowAutoFix(); - } - - /** - * Sets the size of the visible tab (component). As the tab is set to - * position: absolute (to work around a firefox flickering bug) we must keep - * this up-to-date by hand. - */ - private void updateOpenTabSize() { - /* - * The overflow=auto element must have a height specified, otherwise it - * will be just as high as the contents and no scrollbars will appear - */ - int height = -1; - int width = -1; - int minWidth = 0; - - if (!isDynamicHeight()) { - height = renderSpace.getHeight(); - } - if (!isDynamicWidth()) { - width = renderSpace.getWidth(); - } else { - /* - * If the tabbar is wider than the content we need to use the tabbar - * width as minimum width so scrollbars get placed correctly (at the - * right edge). - */ - minWidth = tb.getOffsetWidth() - getContentAreaBorderWidth(); - } - tp.fixVisibleTabSize(width, height, minWidth); - - } - - /** - * Layouts the tab-scroller elements, and applies styles. - */ - private void updateTabScroller() { - if (width != null) { - DOM.setStyleAttribute(tabs, "width", width); - } - if (scrollerIndex > tb.getTabCount()) { - scrollerIndex = 0; - } - boolean scrolled = isScrolledTabs(); - boolean clipped = isClippedTabs(); - if (tb.isVisible() && (scrolled || clipped)) { - DOM.setStyleAttribute(scroller, "display", ""); - DOM.setElementProperty(scrollerPrev, "className", - SCROLLER_CLASSNAME + (scrolled ? "Prev" : "Prev-disabled")); - DOM.setElementProperty(scrollerNext, "className", - SCROLLER_CLASSNAME + (clipped ? "Next" : "Next-disabled")); - } else { - DOM.setStyleAttribute(scroller, "display", "none"); - } - - if (BrowserInfo.get().isSafari()) { - // fix tab height for safari, bugs sometimes if tabs contain icons - String property = tabs.getStyle().getProperty("height"); - if (property == null || property.equals("")) { - tabs.getStyle().setPropertyPx("height", tb.getOffsetHeight()); - } - /* - * another hack for webkits. tabscroller sometimes drops without - * "shaking it" reproducable in - * com.vaadin.tests.components.tabsheet.TabSheetIcons - */ - final Style style = scroller.getStyle(); - style.setProperty("whiteSpace", "normal"); - DeferredCommand.addCommand(new Command() { - public void execute() { - style.setProperty("whiteSpace", ""); - } - }); - } - - } - - private void showAllTabs() { - scrollerIndex = 0; - Element tr = DOM.getFirstChild(DOM.getFirstChild(tb.getElement())); - for (int i = 0; i < tb.getTabCount(); i++) { - DOM.setStyleAttribute(DOM.getChild(tr, i), "display", ""); - } - } - - private boolean isScrolledTabs() { - return scrollerIndex > 0; - } - - private boolean isClippedTabs() { - return tb.getOffsetWidth() > getOffsetWidth(); - } - - @Override - protected void clearPaintables() { - - int i = tb.getTabCount(); - while (i > 0) { - tb.removeTab(--i); - } - tp.clear(); - - } - - @Override - protected Iterator getPaintableIterator() { - return tp.iterator(); - } - - public boolean hasChildComponent(Widget component) { - if (tp.getWidgetIndex(component) < 0) { - return false; - } else { - return true; - } - } - - public void replaceChildComponent(Widget oldComponent, Widget newComponent) { - tp.replaceComponent(oldComponent, newComponent); - } - - public void updateCaption(Paintable component, UIDL uidl) { - /* Tabsheet does not render its children's captions */ - } - - public boolean requestLayout(Set child) { - if (!isDynamicHeight() && !isDynamicWidth()) { - /* - * If the height and width has been specified for this container the - * child components cannot make the size of the layout change - */ - - return true; - } - - updateOpenTabSize(); - - if (renderInformation.updateSize(getElement())) { - /* - * Size has changed so we let the child components know about the - * new size. - */ - iLayout(); - client.runDescendentsLayout(this); - - return false; - } else { - /* - * Size has not changed so we do not need to propagate the event - * further - */ - return true; - } - - } - - private int borderW = -1; - - private int getContentAreaBorderWidth() { - if (borderW < 0) { - borderW = Util.measureHorizontalBorder(contentNode); - } - return borderW; - } - - private RenderSpace renderSpace = new RenderSpace(0, 0, true); - - public RenderSpace getAllocatedSpace(Widget child) { - // All tabs have equal amount of space allocated - return renderSpace; - } - - @Override - protected int getTabCount() { - return tb.getWidgetCount(); - } - - @Override - protected Paintable getTab(int index) { - if (tp.getWidgetCount() > index) { - return (Paintable) tp.getWidget(index); - } - return null; - } - - @Override - protected void removeTab(int index) { - tb.removeTab(index); - /* - * This must be checked because renderTab automatically removes the - * active tab content when it changes - */ - if (tp.getWidgetCount() > index) { - tp.remove(index); - } - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ITabsheetBase.java b/src/com/vaadin/terminal/gwt/client/ui/ITabsheetBase.java deleted file mode 100644 index 081f5977aa..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ITabsheetBase.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.vaadin.terminal.gwt.client.ui; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.ui.ComplexPanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.Container; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; - -abstract class ITabsheetBase extends ComplexPanel implements Container { - - String id; - ApplicationConnection client; - - protected final ArrayList tabKeys = new ArrayList(); - protected int activeTabIndex = 0; - protected boolean disabled; - protected boolean readonly; - protected Set disabledTabKeys = new HashSet(); - protected boolean cachedUpdate = false; - - public ITabsheetBase(String classname) { - setElement(DOM.createDiv()); - setStylePrimaryName(classname); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - this.client = client; - - // Ensure correct implementation - cachedUpdate = client.updateComponent(this, uidl, true); - if (cachedUpdate) { - return; - } - - // Update member references - id = uidl.getId(); - disabled = uidl.hasAttribute("disabled"); - - // Render content - final UIDL tabs = uidl.getChildUIDL(0); - - // Paintables in the TabSheet before update - ArrayList oldPaintables = new ArrayList(); - for (Iterator iterator = getPaintableIterator(); iterator.hasNext();) { - oldPaintables.add(iterator.next()); - } - - // Clear previous values - tabKeys.clear(); - disabledTabKeys.clear(); - - int index = 0; - for (final Iterator it = tabs.getChildIterator(); it.hasNext();) { - final UIDL tab = (UIDL) it.next(); - final String key = tab.getStringAttribute("key"); - final boolean selected = tab.getBooleanAttribute("selected"); - final boolean hidden = tab.getBooleanAttribute("hidden"); - - if (tab.getBooleanAttribute("disabled")) { - disabledTabKeys.add(key); - } - - tabKeys.add(key); - - if (selected) { - activeTabIndex = index; - } - renderTab(tab, index, selected, hidden); - index++; - } - - int tabCount = getTabCount(); - while (tabCount-- > index) { - removeTab(index); - } - - for (int i = 0; i < getTabCount(); i++) { - Paintable p = getTab(i); - oldPaintables.remove(p); - } - - // Perform unregister for any paintables removed during update - for (Iterator iterator = oldPaintables.iterator(); iterator.hasNext();) { - Object oldPaintable = iterator.next(); - if (oldPaintable instanceof Paintable) { - Widget w = (Widget) oldPaintable; - if (w.isAttached()) { - w.removeFromParent(); - } - client.unregisterPaintable((Paintable) oldPaintable); - } - } - - } - - /** - * @return a list of currently shown Paintables - */ - abstract protected Iterator getPaintableIterator(); - - /** - * Clears current tabs and contents - */ - abstract protected void clearPaintables(); - - /** - * Implement in extending classes. This method should render needed elements - * and set the visibility of the tab according to the 'selected' parameter. - */ - protected abstract void renderTab(final UIDL tabUidl, int index, - boolean selected, boolean hidden); - - /** - * Implement in extending classes. This method should render any previously - * non-cached content and set the activeTabIndex property to the specified - * index. - */ - protected abstract void selectTab(int index, final UIDL contentUidl); - - /** - * Implement in extending classes. This method should return the number of - * tabs currently rendered. - */ - protected abstract int getTabCount(); - - /** - * Implement in extending classes. This method should return the Paintable - * corresponding to the given index. - */ - protected abstract Paintable getTab(int index); - - /** - * Implement in extending classes. This method should remove the rendered - * tab with the specified index. - */ - protected abstract void removeTab(int index); -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ITabsheetPanel.java b/src/com/vaadin/terminal/gwt/client/ui/ITabsheetPanel.java deleted file mode 100644 index 776e06b17b..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ITabsheetPanel.java +++ /dev/null @@ -1,183 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.ui.ComplexPanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.Util; - -/** - * A panel that displays all of its child widgets in a 'deck', where only one - * can be visible at a time. It is used by - * {@link com.vaadin.terminal.gwt.client.ui.ITabsheet}. - * - * This class has the same basic functionality as the GWT DeckPanel - * {@link com.google.gwt.user.client.ui.DeckPanel}, with the exception that it - * doesn't manipulate the child widgets' width and height attributes. - */ -public class ITabsheetPanel extends ComplexPanel { - - private Widget visibleWidget; - - /** - * Creates an empty tabsheet panel. - */ - public ITabsheetPanel() { - setElement(DOM.createDiv()); - } - - /** - * Adds the specified widget to the deck. - * - * @param w - * the widget to be added - */ - @Override - public void add(Widget w) { - Element el = createContainerElement(); - DOM.appendChild(getElement(), el); - super.add(w, el); - } - - private Element createContainerElement() { - Element el = DOM.createDiv(); - DOM.setStyleAttribute(el, "position", "absolute"); - DOM.setStyleAttribute(el, "overflow", "auto"); - hide(el); - return el; - } - - /** - * Gets the index of the currently-visible widget. - * - * @return the visible widget's index - */ - public int getVisibleWidget() { - return getWidgetIndex(visibleWidget); - } - - /** - * Inserts a widget before the specified index. - * - * @param w - * the widget to be inserted - * @param beforeIndex - * the index before which it will be inserted - * @throws IndexOutOfBoundsException - * if beforeIndex is out of range - */ - public void insert(Widget w, int beforeIndex) { - Element el = createContainerElement(); - DOM.insertChild(getElement(), el, beforeIndex); - super.insert(w, el, beforeIndex, false); - } - - @Override - public boolean remove(Widget w) { - Element child = w.getElement(); - Element parent = null; - if (child != null) { - parent = DOM.getParent(child); - } - final boolean removed = super.remove(w); - if (removed) { - if (visibleWidget == w) { - visibleWidget = null; - } - if (parent != null) { - DOM.removeChild(getElement(), parent); - } - } - return removed; - } - - /** - * Shows the widget at the specified index. This causes the currently- - * visible widget to be hidden. - * - * @param index - * the index of the widget to be shown - */ - public void showWidget(int index) { - checkIndexBoundsForAccess(index); - Widget newVisible = getWidget(index); - if (visibleWidget != newVisible) { - if (visibleWidget != null) { - hide(DOM.getParent(visibleWidget.getElement())); - } - visibleWidget = newVisible; - unHide(DOM.getParent(visibleWidget.getElement())); - } - } - - private void hide(Element e) { - DOM.setStyleAttribute(e, "visibility", "hidden"); - DOM.setStyleAttribute(e, "top", "-100000px"); - DOM.setStyleAttribute(e, "left", "-100000px"); - } - - private void unHide(Element e) { - DOM.setStyleAttribute(e, "top", "0px"); - DOM.setStyleAttribute(e, "left", "0px"); - DOM.setStyleAttribute(e, "visibility", ""); - } - - public void fixVisibleTabSize(int width, int height, int minWidth) { - if (visibleWidget == null) { - return; - } - - boolean dynamicHeight = false; - - if (height < 0) { - height = visibleWidget.getOffsetHeight(); - dynamicHeight = true; - } - if (width < 0) { - width = visibleWidget.getOffsetWidth(); - } - if (width < minWidth) { - width = minWidth; - } - - Element wrapperDiv = (Element) visibleWidget.getElement() - .getParentElement(); - - // width first - getElement().getStyle().setPropertyPx("width", width); - wrapperDiv.getStyle().setPropertyPx("width", width); - - if (dynamicHeight) { - // height of widget might have changed due wrapping - height = visibleWidget.getOffsetHeight(); - } - // i-tabsheet-tabsheetpanel height - getElement().getStyle().setPropertyPx("height", height); - - // widget wrapper height - wrapperDiv.getStyle().setPropertyPx("height", height); - runWebkitOverflowAutoFix(); - } - - public void runWebkitOverflowAutoFix() { - if (visibleWidget != null) { - Util.runWebkitOverflowAutoFix(DOM.getParent(visibleWidget - .getElement())); - } - - } - - public void replaceComponent(Widget oldComponent, Widget newComponent) { - boolean isVisible = (visibleWidget == oldComponent); - int widgetIndex = getWidgetIndex(oldComponent); - remove(oldComponent); - insert(newComponent, widgetIndex); - if (isVisible) { - showWidget(widgetIndex); - } - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ITextArea.java b/src/com/vaadin/terminal/gwt/client/ui/ITextArea.java deleted file mode 100644 index 7a03f85542..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ITextArea.java +++ /dev/null @@ -1,72 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import com.google.gwt.user.client.Command; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.DeferredCommand; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.UIDL; - -/** - * This class represents a multiline textfield (textarea). - * - * TODO consider replacing this with a RichTextArea based implementation. IE - * does not support CSS height for textareas in Strict mode :-( - * - * @author IT Mill Ltd. - * - */ -public class ITextArea extends ITextField { - public static final String CLASSNAME = "i-textarea"; - - public ITextArea() { - super(DOM.createTextArea()); - setStyleName(CLASSNAME); - } - - @Override - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - // Call parent renderer explicitly - super.updateFromUIDL(uidl, client); - - if (uidl.hasAttribute("rows")) { - setRows(new Integer(uidl.getStringAttribute("rows")).intValue()); - } - - if (getMaxLength() >= 0) { - sinkEvents(Event.ONKEYPRESS); - } - } - - public void setRows(int rows) { - setRows(getElement(), rows); - } - - private native void setRows(Element e, int r) - /*-{ - try { - if(e.tagName.toLowerCase() == "textarea") - e.rows = r; - } catch (e) {} - }-*/; - - @Override - public void onBrowserEvent(Event event) { - if (getMaxLength() >= 0 && event.getTypeInt() == Event.ONKEYPRESS) { - DeferredCommand.addCommand(new Command() { - public void execute() { - if (getText().length() > getMaxLength()) { - setText(getText().substring(0, getMaxLength())); - } - } - }); - } - super.onBrowserEvent(event); - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ITextField.java b/src/com/vaadin/terminal/gwt/client/ui/ITextField.java deleted file mode 100644 index 74cd1cf329..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ITextField.java +++ /dev/null @@ -1,270 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.ChangeListener; -import com.google.gwt.user.client.ui.FocusListener; -import com.google.gwt.user.client.ui.TextBoxBase; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.ITooltip; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; - -/** - * This class represents a basic text input field with one row. - * - * @author IT Mill Ltd. - * - */ -public class ITextField extends TextBoxBase implements Paintable, Field, - ChangeListener, FocusListener { - - /** - * The input node CSS classname. - */ - public static final String CLASSNAME = "i-textfield"; - /** - * This CSS classname is added to the input node on hover. - */ - public static final String CLASSNAME_FOCUS = "focus"; - - protected String id; - - protected ApplicationConnection client; - - private String valueBeforeEdit = null; - - private boolean immediate = false; - private int extraHorizontalPixels = -1; - private int extraVerticalPixels = -1; - private int maxLength = -1; - - private static final String CLASSNAME_PROMPT = "prompt"; - private static final String ATTR_INPUTPROMPT = "prompt"; - private String inputPrompt = null; - private boolean prompting = false; - - public ITextField() { - this(DOM.createInputText()); - } - - protected ITextField(Element node) { - super(node); - if (BrowserInfo.get().isIE()) { - // Fixes IE margin problem (#2058) - DOM.setStyleAttribute(node, "marginTop", "-1px"); - DOM.setStyleAttribute(node, "marginBottom", "-1px"); - } - setStyleName(CLASSNAME); - addChangeListener(this); - addFocusListener(this); - sinkEvents(ITooltip.TOOLTIP_EVENTS); - } - - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - if (client != null) { - client.handleTooltipEvent(event, this); - } - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - this.client = client; - id = uidl.getId(); - - if (client.updateComponent(this, uidl, true)) { - return; - } - - if (uidl.getBooleanAttribute("readonly")) { - setReadOnly(true); - } else { - setReadOnly(false); - } - - inputPrompt = uidl.getStringAttribute(ATTR_INPUTPROMPT); - - setMaxLength(uidl.hasAttribute("maxLength") ? uidl - .getIntAttribute("maxLength") : -1); - - immediate = uidl.getBooleanAttribute("immediate"); - - if (uidl.hasAttribute("cols")) { - setColumns(new Integer(uidl.getStringAttribute("cols")).intValue()); - } - - String text = uidl.getStringVariable("text"); - prompting = inputPrompt != null && (text == null || text.equals("")); - if (prompting) { - setText(inputPrompt); - addStyleDependentName(CLASSNAME_PROMPT); - } else { - setText(text); - removeStyleDependentName(CLASSNAME_PROMPT); - } - valueBeforeEdit = uidl.getStringVariable("text"); - } - - private void setMaxLength(int newMaxLength) { - if (newMaxLength > 0) { - maxLength = newMaxLength; - if (getElement().getTagName().toLowerCase().equals("textarea")) { - // NOP no maxlength property for textarea - } else { - getElement().setPropertyInt("maxLength", maxLength); - } - } else if (maxLength != -1) { - if (getElement().getTagName().toLowerCase().equals("textarea")) { - // NOP no maxlength property for textarea - } else { - getElement().setAttribute("maxlength", ""); - } - maxLength = -1; - } - - } - - protected int getMaxLength() { - return maxLength; - } - - public void onChange(Widget sender) { - if (client != null && id != null) { - String newText = getText(); - if (!prompting && newText != null - && !newText.equals(valueBeforeEdit)) { - client.updateVariable(id, "text", getText(), immediate); - valueBeforeEdit = newText; - } - } - } - - private static ITextField focusedTextField; - - public static void flushChangesFromFocusedTextField() { - if (focusedTextField != null) { - focusedTextField.onChange(null); - } - } - - public void onFocus(Widget sender) { - addStyleDependentName(CLASSNAME_FOCUS); - if (prompting) { - setText(""); - removeStyleDependentName(CLASSNAME_PROMPT); - } - focusedTextField = this; - } - - public void onLostFocus(Widget sender) { - removeStyleDependentName(CLASSNAME_FOCUS); - focusedTextField = null; - String text = getText(); - prompting = inputPrompt != null && (text == null || "".equals(text)); - if (prompting) { - setText(inputPrompt); - addStyleDependentName(CLASSNAME_PROMPT); - } - onChange(sender); - } - - public void setColumns(int columns) { - setColumns(getElement(), columns); - } - - private native void setColumns(Element e, int c) - /*-{ - try { - switch(e.tagName.toLowerCase()) { - case "input": - //e.size = c; - e.style.width = c+"em"; - break; - case "textarea": - //e.cols = c; - e.style.width = c+"em"; - break; - default:; - } - } catch (e) {} - }-*/; - - /** - * @return space used by components paddings and borders - */ - private int getExtraHorizontalPixels() { - if (extraHorizontalPixels < 0) { - detectExtraSizes(); - } - return extraHorizontalPixels; - } - - /** - * @return space used by components paddings and borders - */ - private int getExtraVerticalPixels() { - if (extraVerticalPixels < 0) { - detectExtraSizes(); - } - return extraVerticalPixels; - } - - /** - * Detects space used by components paddings and borders. Used when - * relational size are used. - */ - private void detectExtraSizes() { - Element clone = Util.cloneNode(getElement(), false); - DOM.setElementAttribute(clone, "id", ""); - DOM.setStyleAttribute(clone, "visibility", "hidden"); - DOM.setStyleAttribute(clone, "position", "absolute"); - // due FF3 bug set size to 10px and later subtract it from extra pixels - DOM.setStyleAttribute(clone, "width", "10px"); - DOM.setStyleAttribute(clone, "height", "10px"); - DOM.appendChild(DOM.getParent(getElement()), clone); - extraHorizontalPixels = DOM.getElementPropertyInt(clone, "offsetWidth") - 10; - extraVerticalPixels = DOM.getElementPropertyInt(clone, "offsetHeight") - 10; - - DOM.removeChild(DOM.getParent(getElement()), clone); - } - - @Override - public void setHeight(String height) { - if (height.endsWith("px")) { - int h = Integer.parseInt(height.substring(0, height.length() - 2)); - h -= getExtraVerticalPixels(); - if (h < 0) { - h = 0; - } - - super.setHeight(h + "px"); - } else { - super.setHeight(height); - } - } - - @Override - public void setWidth(String width) { - if (width.endsWith("px")) { - int w = Integer.parseInt(width.substring(0, width.length() - 2)); - w -= getExtraHorizontalPixels(); - if (w < 0) { - w = 0; - } - - super.setWidth(w + "px"); - } else { - super.setWidth(width); - } - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ITextualDate.java b/src/com/vaadin/terminal/gwt/client/ui/ITextualDate.java deleted file mode 100644 index 311390cbcc..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ITextualDate.java +++ /dev/null @@ -1,301 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.Date; - -import com.google.gwt.i18n.client.DateTimeFormat; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.ui.ChangeListener; -import com.google.gwt.user.client.ui.TextBox; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.ClientExceptionHandler; -import com.vaadin.terminal.gwt.client.ContainerResizedListener; -import com.vaadin.terminal.gwt.client.Focusable; -import com.vaadin.terminal.gwt.client.LocaleNotLoadedException; -import com.vaadin.terminal.gwt.client.LocaleService; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; - -public class ITextualDate extends IDateField implements Paintable, Field, - ChangeListener, ContainerResizedListener, Focusable { - - private static final String PARSE_ERROR_CLASSNAME = CLASSNAME - + "-parseerror"; - - private final TextBox text; - - private String formatStr; - - private String width; - - private boolean needLayout; - - protected int fieldExtraWidth = -1; - - public ITextualDate() { - super(); - text = new TextBox(); - // use normal textfield styles as a basis - text.setStyleName(ITextField.CLASSNAME); - // add datefield spesific style name also - text.addStyleName(CLASSNAME + "-textfield"); - text.addChangeListener(this); - add(text); - } - - @Override - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - - int origRes = currentResolution; - super.updateFromUIDL(uidl, client); - if (origRes != currentResolution) { - // force recreating format string - formatStr = null; - } - if (uidl.hasAttribute("format")) { - formatStr = uidl.getStringAttribute("format"); - } - - buildDate(); - // not a FocusWidget -> needs own tabindex handling - if (uidl.hasAttribute("tabindex")) { - text.setTabIndex(uidl.getIntAttribute("tabindex")); - } - } - - protected String getFormatString() { - if (formatStr == null) { - if (currentResolution == RESOLUTION_YEAR) { - formatStr = "yyyy"; // force full year - } else { - - try { - String frmString = LocaleService - .getDateFormat(currentLocale); - frmString = cleanFormat(frmString); - String delim = LocaleService - .getClockDelimiter(currentLocale); - - if (currentResolution >= RESOLUTION_HOUR) { - if (dts.isTwelveHourClock()) { - frmString += " hh"; - } else { - frmString += " HH"; - } - if (currentResolution >= RESOLUTION_MIN) { - frmString += ":mm"; - if (currentResolution >= RESOLUTION_SEC) { - frmString += ":ss"; - if (currentResolution >= RESOLUTION_MSEC) { - frmString += ".SSS"; - } - } - } - if (dts.isTwelveHourClock()) { - frmString += " aaa"; - } - - } - - formatStr = frmString; - } catch (LocaleNotLoadedException e) { - ClientExceptionHandler.displayError(e); - } - } - } - return formatStr; - } - - /** - * - */ - protected void buildDate() { - removeStyleName(PARSE_ERROR_CLASSNAME); - // Create the initial text for the textfield - String dateText; - if (date != null) { - dateText = DateTimeFormat.getFormat(getFormatString()).format(date); - } else { - dateText = ""; - } - - text.setText(dateText); - text.setEnabled(enabled && !readonly); - - if (readonly) { - text.addStyleName("i-readonly"); - } else { - text.removeStyleName("i-readonly"); - } - - } - - public void onChange(Widget sender) { - if (sender == text) { - if (!text.getText().equals("")) { - try { - DateTimeFormat format = DateTimeFormat - .getFormat(getFormatString()); - date = format.parse(text.getText()); - long stamp = date.getTime(); - if (stamp == 0) { - // If date parsing fails in firefox the stamp will be 0 - date = null; - addStyleName(PARSE_ERROR_CLASSNAME); - } else { - // remove possibly added invalid value indication - removeStyleName(PARSE_ERROR_CLASSNAME); - } - } catch (final Exception e) { - ClientExceptionHandler.displayError(e.getMessage()); - - addStyleName(PARSE_ERROR_CLASSNAME); - // this is a hack that may eventually be removed - client.updateVariable(id, "lastInvalidDateString", text - .getText(), false); - date = null; - } - } else { - date = null; - // remove possibly added invalid value indication - removeStyleName(PARSE_ERROR_CLASSNAME); - } - // always send the date string - client.updateVariable(id, "dateString", text.getText(), false); - - if (date != null) { - showingDate = new Date(date.getTime()); - } - - // Update variables - // (only the smallest defining resolution needs to be - // immediate) - client.updateVariable(id, "year", - date != null ? date.getYear() + 1900 : -1, - currentResolution == IDateField.RESOLUTION_YEAR - && immediate); - if (currentResolution >= IDateField.RESOLUTION_MONTH) { - client.updateVariable(id, "month", date != null ? date - .getMonth() + 1 : -1, - currentResolution == IDateField.RESOLUTION_MONTH - && immediate); - } - if (currentResolution >= IDateField.RESOLUTION_DAY) { - client.updateVariable(id, "day", date != null ? date.getDate() - : -1, currentResolution == IDateField.RESOLUTION_DAY - && immediate); - } - if (currentResolution >= IDateField.RESOLUTION_HOUR) { - client.updateVariable(id, "hour", date != null ? date - .getHours() : -1, - currentResolution == IDateField.RESOLUTION_HOUR - && immediate); - } - if (currentResolution >= IDateField.RESOLUTION_MIN) { - client.updateVariable(id, "min", date != null ? date - .getMinutes() : -1, - currentResolution == IDateField.RESOLUTION_MIN - && immediate); - } - if (currentResolution >= IDateField.RESOLUTION_SEC) { - client.updateVariable(id, "sec", date != null ? date - .getSeconds() : -1, - currentResolution == IDateField.RESOLUTION_SEC - && immediate); - } - if (currentResolution == IDateField.RESOLUTION_MSEC) { - client.updateVariable(id, "msec", - date != null ? getMilliseconds() : -1, immediate); - } - - } - } - - private String cleanFormat(String format) { - // Remove unnecessary d & M if resolution is too low - if (currentResolution < IDateField.RESOLUTION_DAY) { - format = format.replaceAll("d", ""); - } - if (currentResolution < IDateField.RESOLUTION_MONTH) { - format = format.replaceAll("M", ""); - } - - // Remove unsupported patterns - // TODO support for 'G', era designator (used at least in Japan) - format = format.replaceAll("[GzZwWkK]", ""); - - // Remove extra delimiters ('/' and '.') - while (format.startsWith("/") || format.startsWith(".") - || format.startsWith("-")) { - format = format.substring(1); - } - while (format.endsWith("/") || format.endsWith(".") - || format.endsWith("-")) { - format = format.substring(0, format.length() - 1); - } - - // Remove duplicate delimiters - format = format.replaceAll("//", "/"); - format = format.replaceAll("\\.\\.", "."); - format = format.replaceAll("--", "-"); - - return format.trim(); - } - - @Override - public void setWidth(String newWidth) { - if (!"".equals(newWidth) && (width == null || !newWidth.equals(width))) { - if (BrowserInfo.get().isIE6()) { - // in IE6 cols ~ min-width - DOM.setElementProperty(text.getElement(), "size", "1"); - } - needLayout = true; - width = newWidth; - super.setWidth(width); - iLayout(); - if (newWidth.indexOf("%") < 0) { - needLayout = false; - } - } else { - if ("".equals(newWidth) && width != null && !"".equals(width)) { - if (BrowserInfo.get().isIE6()) { - // revert IE6 hack - DOM.setElementProperty(text.getElement(), "size", ""); - } - super.setWidth(""); - needLayout = true; - iLayout(); - needLayout = false; - width = null; - } - } - } - - /** - * Returns pixels in x-axis reserved for other than textfield content. - * - * @return extra width in pixels - */ - protected int getFieldExtraWidth() { - if (fieldExtraWidth < 0) { - text.setWidth("0px"); - fieldExtraWidth = text.getOffsetWidth(); - } - return fieldExtraWidth; - } - - public void iLayout() { - if (needLayout) { - text.setWidth((getOffsetWidth() - getFieldExtraWidth()) + "px"); - } - } - - public void focus() { - text.setFocus(true); - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ITime.java b/src/com/vaadin/terminal/gwt/client/ui/ITime.java deleted file mode 100644 index 43c1f9f261..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ITime.java +++ /dev/null @@ -1,317 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.Date; - -import com.google.gwt.user.client.ui.ChangeListener; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.ListBox; -import com.google.gwt.user.client.ui.Widget; - -public class ITime extends FlowPanel implements ChangeListener { - - private final IDateField datefield; - - private ListBox hours; - - private ListBox mins; - - private ListBox sec; - - private ListBox msec; - - private ListBox ampm; - - private int resolution = IDateField.RESOLUTION_HOUR; - - private boolean readonly; - - public ITime(IDateField parent) { - super(); - datefield = parent; - setStyleName(IDateField.CLASSNAME + "-time"); - } - - private void buildTime(boolean redraw) { - final boolean thc = datefield.getDateTimeService().isTwelveHourClock(); - if (redraw) { - clear(); - final int numHours = thc ? 12 : 24; - hours = new ListBox(); - hours.setStyleName(INativeSelect.CLASSNAME); - for (int i = 0; i < numHours; i++) { - hours.addItem((i < 10) ? "0" + i : "" + i); - } - hours.addChangeListener(this); - if (thc) { - ampm = new ListBox(); - ampm.setStyleName(INativeSelect.CLASSNAME); - final String[] ampmText = datefield.getDateTimeService() - .getAmPmStrings(); - ampm.addItem(ampmText[0]); - ampm.addItem(ampmText[1]); - ampm.addChangeListener(this); - } - - if (datefield.getCurrentResolution() >= IDateField.RESOLUTION_MIN) { - mins = new ListBox(); - mins.setStyleName(INativeSelect.CLASSNAME); - for (int i = 0; i < 60; i++) { - mins.addItem((i < 10) ? "0" + i : "" + i); - } - mins.addChangeListener(this); - } - if (datefield.getCurrentResolution() >= IDateField.RESOLUTION_SEC) { - sec = new ListBox(); - sec.setStyleName(INativeSelect.CLASSNAME); - for (int i = 0; i < 60; i++) { - sec.addItem((i < 10) ? "0" + i : "" + i); - } - sec.addChangeListener(this); - } - if (datefield.getCurrentResolution() == IDateField.RESOLUTION_MSEC) { - msec = new ListBox(); - msec.setStyleName(INativeSelect.CLASSNAME); - for (int i = 0; i < 1000; i++) { - if (i < 10) { - msec.addItem("00" + i); - } else if (i < 100) { - msec.addItem("0" + i); - } else { - msec.addItem("" + i); - } - } - msec.addChangeListener(this); - } - - final String delimiter = datefield.getDateTimeService() - .getClockDelimeter(); - final boolean ro = datefield.isReadonly(); - - if (ro) { - int h = 0; - if (datefield.getCurrentDate() != null) { - h = datefield.getCurrentDate().getHours(); - } - if (thc) { - h -= h < 12 ? 0 : 12; - } - add(new ILabel(h < 10 ? "0" + h : "" + h)); - } else { - add(hours); - } - - if (datefield.getCurrentResolution() >= IDateField.RESOLUTION_MIN) { - add(new ILabel(delimiter)); - if (ro) { - final int m = mins.getSelectedIndex(); - add(new ILabel(m < 10 ? "0" + m : "" + m)); - } else { - add(mins); - } - } - if (datefield.getCurrentResolution() >= IDateField.RESOLUTION_SEC) { - add(new ILabel(delimiter)); - if (ro) { - final int s = sec.getSelectedIndex(); - add(new ILabel(s < 10 ? "0" + s : "" + s)); - } else { - add(sec); - } - } - if (datefield.getCurrentResolution() == IDateField.RESOLUTION_MSEC) { - add(new ILabel(".")); - if (ro) { - final int m = datefield.getMilliseconds(); - final String ms = m < 100 ? "0" + m : "" + m; - add(new ILabel(m < 10 ? "0" + ms : ms)); - } else { - add(msec); - } - } - if (datefield.getCurrentResolution() == IDateField.RESOLUTION_HOUR) { - add(new ILabel(delimiter + "00")); // o'clock - } - if (thc) { - add(new ILabel(" ")); - if (ro) { - add(new ILabel(ampm.getItemText(datefield.getCurrentDate() - .getHours() < 12 ? 0 : 1))); - } else { - add(ampm); - } - } - - if (ro) { - return; - } - } - - // Update times - Date cdate = datefield.getCurrentDate(); - boolean selected = true; - if (cdate == null) { - cdate = new Date(); - selected = false; - } - if (thc) { - int h = cdate.getHours(); - ampm.setSelectedIndex(h < 12 ? 0 : 1); - h -= ampm.getSelectedIndex() * 12; - hours.setSelectedIndex(h); - } else { - hours.setSelectedIndex(cdate.getHours()); - } - if (datefield.getCurrentResolution() >= IDateField.RESOLUTION_MIN) { - mins.setSelectedIndex(cdate.getMinutes()); - } - if (datefield.getCurrentResolution() >= IDateField.RESOLUTION_SEC) { - sec.setSelectedIndex(cdate.getSeconds()); - } - if (datefield.getCurrentResolution() == IDateField.RESOLUTION_MSEC) { - if (selected) { - msec.setSelectedIndex(datefield.getMilliseconds()); - } else { - msec.setSelectedIndex(0); - } - } - if (thc) { - ampm.setSelectedIndex(cdate.getHours() < 12 ? 0 : 1); - } - - if (datefield.isReadonly() && !redraw) { - // Do complete redraw when in read-only status - clear(); - final String delimiter = datefield.getDateTimeService() - .getClockDelimeter(); - - int h = cdate.getHours(); - if (thc) { - h -= h < 12 ? 0 : 12; - } - add(new ILabel(h < 10 ? "0" + h : "" + h)); - - if (datefield.getCurrentResolution() >= IDateField.RESOLUTION_MIN) { - add(new ILabel(delimiter)); - final int m = mins.getSelectedIndex(); - add(new ILabel(m < 10 ? "0" + m : "" + m)); - } - if (datefield.getCurrentResolution() >= IDateField.RESOLUTION_SEC) { - add(new ILabel(delimiter)); - final int s = sec.getSelectedIndex(); - add(new ILabel(s < 10 ? "0" + s : "" + s)); - } - if (datefield.getCurrentResolution() == IDateField.RESOLUTION_MSEC) { - add(new ILabel(".")); - final int m = datefield.getMilliseconds(); - final String ms = m < 100 ? "0" + m : "" + m; - add(new ILabel(m < 10 ? "0" + ms : ms)); - } - if (datefield.getCurrentResolution() == IDateField.RESOLUTION_HOUR) { - add(new ILabel(delimiter + "00")); // o'clock - } - if (thc) { - add(new ILabel(" ")); - add(new ILabel(ampm.getItemText(cdate.getHours() < 12 ? 0 : 1))); - } - } - - final boolean enabled = datefield.isEnabled(); - hours.setEnabled(enabled); - if (mins != null) { - mins.setEnabled(enabled); - } - if (sec != null) { - sec.setEnabled(enabled); - } - if (msec != null) { - msec.setEnabled(enabled); - } - if (ampm != null) { - ampm.setEnabled(enabled); - } - - } - - public void updateTime(boolean redraw) { - buildTime(redraw || resolution != datefield.getCurrentResolution() - || readonly != datefield.isReadonly()); - if (datefield instanceof ITextualDate) { - ((ITextualDate) datefield).buildDate(); - } - resolution = datefield.getCurrentResolution(); - readonly = datefield.isReadonly(); - } - - public void onChange(Widget sender) { - if (datefield.getCurrentDate() == null) { - // was null on server, need to set - Date now = datefield.getShowingDate(); - if (now == null) { - now = new Date(); - datefield.setShowingDate(now); - } - datefield.setCurrentDate(new Date(now.getTime())); - - // Init variables with current time - datefield.getClient().updateVariable(datefield.getId(), "year", - now.getYear() + 1900, false); - datefield.getClient().updateVariable(datefield.getId(), "month", - now.getMonth() + 1, false); - datefield.getClient().updateVariable(datefield.getId(), "day", - now.getDate(), false); - datefield.getClient().updateVariable(datefield.getId(), "hour", - now.getHours(), false); - datefield.getClient().updateVariable(datefield.getId(), "min", - now.getMinutes(), false); - datefield.getClient().updateVariable(datefield.getId(), "sec", - now.getSeconds(), false); - datefield.getClient().updateVariable(datefield.getId(), "msec", - datefield.getMilliseconds(), false); - } - if (sender == hours) { - int h = hours.getSelectedIndex(); - if (datefield.getDateTimeService().isTwelveHourClock()) { - h = h + ampm.getSelectedIndex() * 12; - } - datefield.getCurrentDate().setHours(h); - datefield.getShowingDate().setHours(h); - datefield.getClient().updateVariable(datefield.getId(), "hour", h, - datefield.isImmediate()); - updateTime(false); - } else if (sender == mins) { - final int m = mins.getSelectedIndex(); - datefield.getCurrentDate().setMinutes(m); - datefield.getShowingDate().setMinutes(m); - datefield.getClient().updateVariable(datefield.getId(), "min", m, - datefield.isImmediate()); - updateTime(false); - } else if (sender == sec) { - final int s = sec.getSelectedIndex(); - datefield.getCurrentDate().setSeconds(s); - datefield.getShowingDate().setSeconds(s); - datefield.getClient().updateVariable(datefield.getId(), "sec", s, - datefield.isImmediate()); - updateTime(false); - } else if (sender == msec) { - final int ms = msec.getSelectedIndex(); - datefield.setMilliseconds(ms); - datefield.setShowingMilliseconds(ms); - datefield.getClient().updateVariable(datefield.getId(), "msec", ms, - datefield.isImmediate()); - updateTime(false); - } else if (sender == ampm) { - final int h = hours.getSelectedIndex() + ampm.getSelectedIndex() - * 12; - datefield.getCurrentDate().setHours(h); - datefield.getShowingDate().setHours(h); - datefield.getClient().updateVariable(datefield.getId(), "hour", h, - datefield.isImmediate()); - updateTime(false); - } - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IToolkitOverlay.java b/src/com/vaadin/terminal/gwt/client/ui/IToolkitOverlay.java deleted file mode 100644 index 8399544e4f..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IToolkitOverlay.java +++ /dev/null @@ -1,313 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import com.google.gwt.animation.client.Animation; -import com.google.gwt.dom.client.Document; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.ui.PopupListener; -import com.google.gwt.user.client.ui.PopupPanel; -import com.google.gwt.user.client.ui.RootPanel; -import com.vaadin.terminal.gwt.client.BrowserInfo; - -/** - * In Toolkit UI this Overlay should always be used for all elements that - * temporary float over other components like context menus etc. This is to deal - * stacking order correctly with IWindow objects. - */ -public class IToolkitOverlay extends PopupPanel { - - /* - * The z-index value from where all overlays live. This can be overridden in - * any extending class. - */ - protected static int Z_INDEX = 20000; - - /* - * Shadow element style. If an extending class wishes to use a different - * style of shadow, it can use setShadowStyle(String) to give the shadow - * element a new style name. - */ - public static final String CLASSNAME_SHADOW = "i-shadow"; - - /* - * The shadow element for this overlay. - */ - private Element shadow; - - /** - * The HTML snippet that is used to render the actual shadow. In consists of - * nine different DIV-elements with the following class names: - * - *
-     *   .i-shadow[-stylename]
-     *   ----------------------------------------------
-     *   | .top-left     |   .top    |     .top-right |
-     *   |---------------|-----------|----------------|
-     *   |               |           |                |
-     *   | .left         |  .center  |         .right |
-     *   |               |           |                |
-     *   |---------------|-----------|----------------|
-     *   | .bottom-left  |  .bottom  |  .bottom-right |
-     *   ----------------------------------------------
-     * 
- * - * See default theme 'shadow.css' for implementation example. - */ - private static final String SHADOW_HTML = "
"; - - public IToolkitOverlay() { - super(); - adjustZIndex(); - } - - public IToolkitOverlay(boolean autoHide) { - super(autoHide); - adjustZIndex(); - } - - public IToolkitOverlay(boolean autoHide, boolean modal) { - super(autoHide, modal); - adjustZIndex(); - } - - public IToolkitOverlay(boolean autoHide, boolean modal, boolean showShadow) { - super(autoHide, modal); - if (showShadow) { - shadow = DOM.createDiv(); - shadow.setClassName(CLASSNAME_SHADOW); - shadow.setInnerHTML(SHADOW_HTML); - DOM.setStyleAttribute(shadow, "position", "absolute"); - - addPopupListener(new PopupListener() { - public void onPopupClosed(PopupPanel sender, boolean autoClosed) { - if (shadow.getParentElement() != null) { - shadow.getParentElement().removeChild(shadow); - } - } - }); - } - adjustZIndex(); - } - - private void adjustZIndex() { - setZIndex(Z_INDEX); - } - - /** - * Set the z-index (visual stack position) for this overlay. - * - * @param zIndex - * The new z-index - */ - protected void setZIndex(int zIndex) { - DOM.setStyleAttribute(getElement(), "zIndex", "" + zIndex); - if (shadow != null) { - DOM.setStyleAttribute(shadow, "zIndex", "" + zIndex); - } - if (BrowserInfo.get().isIE6()) { - adjustIE6Frame(getElement(), zIndex - 1); - } - } - - /** - * Get the z-index (visual stack position) of this overlay. - * - * @return The z-index for this overlay. - */ - private int getZIndex() { - return Integer.parseInt(DOM.getStyleAttribute(getElement(), "zIndex")); - } - - @Override - public void setPopupPosition(int left, int top) { - super.setPopupPosition(left, top); - if (shadow != null) { - updateShadowSizeAndPosition(isAnimationEnabled() ? 0 : 1); - } - } - - @Override - public void show() { - super.show(); - if (shadow != null) { - if (isAnimationEnabled()) { - ShadowAnimation sa = new ShadowAnimation(); - sa.run(200); - } else { - updateShadowSizeAndPosition(1.0); - } - } - if (BrowserInfo.get().isIE6()) { - adjustIE6Frame(getElement(), getZIndex()); - } - } - - @Override - public void setVisible(boolean visible) { - super.setVisible(visible); - if (shadow != null) { - shadow.getStyle().setProperty("visibility", - visible ? "visible" : "hidden"); - } - } - - /* - * Needed to position overlays on top of native SELECT elements in IE6. See - * bug #2004 - */ - private native void adjustIE6Frame(Element popup, int zindex) - /*-{ - // relies on PopupImplIE6 - if(popup.__frame) - popup.__frame.style.zIndex = zindex; - }-*/; - - @Override - public void setWidth(String width) { - super.setWidth(width); - if (shadow != null) { - updateShadowSizeAndPosition(1.0); - } - } - - @Override - public void setHeight(String height) { - super.setHeight(height); - if (shadow != null) { - updateShadowSizeAndPosition(1.0); - } - } - - /** - * Sets the shadow style for this overlay. Will override any previous style - * for the shadow. The default style name is defined by CLASSNAME_SHADOW. - * The given style will be prefixed with CLASSNAME_SHADOW. - * - * @param style - * The new style name for the shadow element. Will be prefixed by - * CLASSNAME_SHADOW, e.g. style=='foobar' -> actual style - * name=='i-shadow-foobar'. - */ - protected void setShadowStyle(String style) { - if (shadow != null) { - shadow.setClassName(CLASSNAME_SHADOW + "-" + style); - } - } - - /* - * Extending classes should always call this method after they change the - * size of overlay without using normal 'setWidth(String)' and - * 'setHeight(String)' methods (if not calling super.setWidth/Height). - */ - protected void updateShadowSizeAndPosition() { - updateShadowSizeAndPosition(1.0); - } - - /** - * Recalculates proper position and dimensions for the shadow element. Can - * be used to animate the shadow, using the 'progress' parameter (used to - * animate the shadow in sync with GWT PopupPanel's default animation - * 'PopupPanel.AnimationType.CENTER'). - * - * @param progress - * A value between 0.0 and 1.0, indicating the progress of the - * animation (0=start, 1=end). - */ - private void updateShadowSizeAndPosition(final double progress) { - // Don't do anything if overlay element is not attached - if (!isAttached()) { - return; - } - // Calculate proper z-index - String zIndex = null; - try { - // Odd behaviour with Windows Hosted Mode forces us to use - // this redundant try/catch block (See dev.itmill.com #2011) - zIndex = DOM.getStyleAttribute(getElement(), "zIndex"); - } catch (Exception ignore) { - // Ignored, will cause no harm - } - if (zIndex == null) { - zIndex = "" + Z_INDEX; - } - // Calculate position and size - if (BrowserInfo.get().isIE()) { - // Shake IE - getOffsetHeight(); - getOffsetWidth(); - } - - int x = getAbsoluteLeft(); - int y = getAbsoluteTop(); - - /* This is needed for IE7 at least */ - // Account for the difference between absolute position and the - // body's positioning context. - x -= Document.get().getBodyOffsetLeft(); - y -= Document.get().getBodyOffsetTop(); - - int width = getOffsetWidth(); - int height = getOffsetHeight(); - - if (width < 0) { - width = 0; - } - if (height < 0) { - height = 0; - } - - // Animate the shadow size - x += (int) (width * (1.0 - progress) / 2.0); - y += (int) (height * (1.0 - progress) / 2.0); - width = (int) (width * progress); - height = (int) (height * progress); - - // Opera needs some shaking to get parts of the shadow showing - // properly - // (ticket #2704) - if (BrowserInfo.get().isOpera()) { - // Clear the height of all middle elements - DOM.getChild(shadow, 3).getStyle().setProperty("height", "auto"); - DOM.getChild(shadow, 4).getStyle().setProperty("height", "auto"); - DOM.getChild(shadow, 5).getStyle().setProperty("height", "auto"); - } - - // Update correct values - DOM.setStyleAttribute(shadow, "zIndex", zIndex); - DOM.setStyleAttribute(shadow, "width", width + "px"); - DOM.setStyleAttribute(shadow, "height", height + "px"); - DOM.setStyleAttribute(shadow, "top", y + "px"); - DOM.setStyleAttribute(shadow, "left", x + "px"); - DOM.setStyleAttribute(shadow, "display", progress < 0.9 ? "none" : ""); - - // Opera fix, part 2 (ticket #2704) - if (BrowserInfo.get().isOpera()) { - // We'll fix the height of all the middle elements - DOM.getChild(shadow, 3).getStyle().setPropertyPx("height", - DOM.getChild(shadow, 3).getOffsetHeight()); - DOM.getChild(shadow, 4).getStyle().setPropertyPx("height", - DOM.getChild(shadow, 4).getOffsetHeight()); - DOM.getChild(shadow, 5).getStyle().setPropertyPx("height", - DOM.getChild(shadow, 5).getOffsetHeight()); - } - - // Attach to dom if not there already - if (shadow.getParentElement() == null) { - RootPanel.get().getElement().insertBefore(shadow, getElement()); - } - - } - - protected class ShadowAnimation extends Animation { - @Override - protected void onUpdate(double progress) { - if (shadow != null) { - updateShadowSizeAndPosition(progress); - } - } - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ITree.java b/src/com/vaadin/terminal/gwt/client/ui/ITree.java deleted file mode 100644 index b322293964..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ITree.java +++ /dev/null @@ -1,469 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.SimplePanel; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.MouseEventDetails; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; - -/** - * - */ -public class ITree extends FlowPanel implements Paintable { - - public static final String CLASSNAME = "i-tree"; - - private Set selectedIds = new HashSet(); - private ApplicationConnection client; - private String paintableId; - private boolean selectable; - private boolean isMultiselect; - - private final HashMap keyToNode = new HashMap(); - - /** - * This map contains captions and icon urls for actions like: * "33_c" -> - * "Edit" * "33_i" -> "http://dom.com/edit.png" - */ - private final HashMap actionMap = new HashMap(); - - private boolean immediate; - - private boolean isNullSelectionAllowed = true; - - private boolean disabled = false; - - private boolean readonly; - - private boolean emitClickEvents; - - private boolean rendering; - - public ITree() { - super(); - setStyleName(CLASSNAME); - } - - private void updateActionMap(UIDL c) { - final Iterator it = c.getChildIterator(); - while (it.hasNext()) { - final UIDL action = (UIDL) it.next(); - final String key = action.getStringAttribute("key"); - final String caption = action.getStringAttribute("caption"); - actionMap.put(key + "_c", caption); - if (action.hasAttribute("icon")) { - // TODO need some uri handling ?? - actionMap.put(key + "_i", client.translateToolkitUri(action - .getStringAttribute("icon"))); - } - } - - } - - public String getActionCaption(String actionKey) { - return actionMap.get(actionKey + "_c"); - } - - public String getActionIcon(String actionKey) { - return actionMap.get(actionKey + "_i"); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - // Ensure correct implementation and let container manage caption - if (client.updateComponent(this, uidl, true)) { - return; - } - - rendering = true; - - this.client = client; - - if (uidl.hasAttribute("partialUpdate")) { - handleUpdate(uidl); - rendering = false; - return; - } - - paintableId = uidl.getId(); - - immediate = uidl.hasAttribute("immediate"); - - disabled = uidl.getBooleanAttribute("disabled"); - readonly = uidl.getBooleanAttribute("readonly"); - emitClickEvents = uidl.getBooleanAttribute("listenClicks"); - - isNullSelectionAllowed = uidl.getBooleanAttribute("nullselect"); - - clear(); - for (final Iterator i = uidl.getChildIterator(); i.hasNext();) { - final UIDL childUidl = (UIDL) i.next(); - if ("actions".equals(childUidl.getTag())) { - updateActionMap(childUidl); - continue; - } - final TreeNode childTree = new TreeNode(); - this.add(childTree); - childTree.updateFromUIDL(childUidl, client); - } - final String selectMode = uidl.getStringAttribute("selectmode"); - selectable = !"none".equals(selectMode); - isMultiselect = "multi".equals(selectMode); - - selectedIds = uidl.getStringArrayVariableAsSet("selected"); - - rendering = false; - - } - - private void handleUpdate(UIDL uidl) { - final TreeNode rootNode = keyToNode.get(uidl - .getStringAttribute("rootKey")); - if (rootNode != null) { - if (!rootNode.getState()) { - // expanding node happened server side - rootNode.setState(true, false); - } - rootNode.renderChildNodes(uidl.getChildIterator()); - } - - if (uidl.hasVariable("selected")) { - // update selection in case selected nodes were not visible - selectedIds = uidl.getStringArrayVariableAsSet("selected"); - } - - } - - public void setSelected(TreeNode treeNode, boolean selected) { - if (selected) { - if (!isMultiselect) { - while (selectedIds.size() > 0) { - final String id = selectedIds.iterator().next(); - final TreeNode oldSelection = keyToNode.get(id); - if (oldSelection != null) { - // can be null if the node is not visible (parent - // collapsed) - oldSelection.setSelected(false); - } - selectedIds.remove(id); - } - } - treeNode.setSelected(true); - selectedIds.add(treeNode.key); - } else { - if (!isNullSelectionAllowed) { - if (!isMultiselect || selectedIds.size() == 1) { - return; - } - } - selectedIds.remove(treeNode.key); - treeNode.setSelected(false); - } - client.updateVariable(paintableId, "selected", selectedIds.toArray(), - immediate); - } - - public boolean isSelected(TreeNode treeNode) { - return selectedIds.contains(treeNode.key); - } - - protected class TreeNode extends SimplePanel implements ActionOwner { - - public static final String CLASSNAME = "i-tree-node"; - - String key; - - private String[] actionKeys = null; - - private boolean childrenLoaded; - - private Element nodeCaptionDiv; - - protected Element nodeCaptionSpan; - - private FlowPanel childNodeContainer; - - private boolean open; - - private Icon icon; - - private Element ie6compatnode; - - public TreeNode() { - constructDom(); - sinkEvents(Event.ONCLICK | Event.ONDBLCLICK | Event.ONMOUSEUP - | Event.ONCONTEXTMENU); - } - - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - if (disabled) { - return; - } - final int type = DOM.eventGetType(event); - final Element target = DOM.eventGetTarget(event); - if (emitClickEvents && target == nodeCaptionSpan - && (type == Event.ONDBLCLICK || type == Event.ONMOUSEUP)) { - fireClick(event); - } - if (type == Event.ONCLICK) { - if (getElement() == target || ie6compatnode == target) { - // state change - toggleState(); - } else if (!readonly && target == nodeCaptionSpan) { - // caption click = selection change && possible click event - toggleSelection(); - } - DOM.eventCancelBubble(event, true); - } else if (type == Event.ONCONTEXTMENU) { - showContextMenu(event); - } - } - - private void fireClick(Event evt) { - // non-immediate iff an immediate select event is going to happen - boolean imm = !immediate - || !selectable - || (!isNullSelectionAllowed && isSelected() && selectedIds - .size() == 1); - MouseEventDetails details = new MouseEventDetails(evt); - client.updateVariable(paintableId, "clickedKey", key, false); - client.updateVariable(paintableId, "clickEvent", - details.toString(), imm); - } - - private void toggleSelection() { - if (selectable) { - ITree.this.setSelected(this, !isSelected()); - } - } - - private void toggleState() { - setState(!getState(), true); - } - - protected void constructDom() { - // workaround for a very weird IE6 issue #1245 - ie6compatnode = DOM.createDiv(); - setStyleName(ie6compatnode, CLASSNAME + "-ie6compatnode"); - DOM.setInnerText(ie6compatnode, " "); - DOM.appendChild(getElement(), ie6compatnode); - - DOM.sinkEvents(ie6compatnode, Event.ONCLICK); - - nodeCaptionDiv = DOM.createDiv(); - DOM.setElementProperty(nodeCaptionDiv, "className", CLASSNAME - + "-caption"); - Element wrapper = DOM.createDiv(); - nodeCaptionSpan = DOM.createSpan(); - DOM.appendChild(getElement(), nodeCaptionDiv); - DOM.appendChild(nodeCaptionDiv, wrapper); - DOM.appendChild(wrapper, nodeCaptionSpan); - - childNodeContainer = new FlowPanel(); - childNodeContainer.setStylePrimaryName(CLASSNAME + "-children"); - setWidget(childNodeContainer); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - setText(uidl.getStringAttribute("caption")); - key = uidl.getStringAttribute("key"); - - keyToNode.put(key, this); - - if (uidl.hasAttribute("al")) { - actionKeys = uidl.getStringArrayAttribute("al"); - } - - if (uidl.getTag().equals("node")) { - if (uidl.getChildCount() == 0) { - childNodeContainer.setVisible(false); - } else { - renderChildNodes(uidl.getChildIterator()); - childrenLoaded = true; - } - } else { - addStyleName(CLASSNAME + "-leaf"); - } - addStyleName(CLASSNAME); - - if (uidl.getBooleanAttribute("expanded") && !getState()) { - setState(true, false); - } - - if (uidl.getBooleanAttribute("selected")) { - setSelected(true); - } - - if (uidl.hasAttribute("icon")) { - if (icon == null) { - icon = new Icon(client); - DOM.insertBefore(DOM.getFirstChild(nodeCaptionDiv), icon - .getElement(), nodeCaptionSpan); - } - icon.setUri(uidl.getStringAttribute("icon")); - } else { - if (icon != null) { - DOM.removeChild(DOM.getFirstChild(nodeCaptionDiv), icon - .getElement()); - icon = null; - } - } - - if (BrowserInfo.get().isIE6() && isAttached()) { - fixWidth(); - } - } - - private void setState(boolean state, boolean notifyServer) { - if (open == state) { - return; - } - if (state) { - if (!childrenLoaded && notifyServer) { - client.updateVariable(paintableId, "requestChildTree", - true, false); - } - if (notifyServer) { - client.updateVariable(paintableId, "expand", - new String[] { key }, true); - } - addStyleName(CLASSNAME + "-expanded"); - childNodeContainer.setVisible(true); - - } else { - removeStyleName(CLASSNAME + "-expanded"); - childNodeContainer.setVisible(false); - if (notifyServer) { - client.updateVariable(paintableId, "collapse", - new String[] { key }, true); - } - } - open = state; - - if (!rendering) { - Util.notifyParentOfSizeChange(ITree.this, false); - } - } - - private boolean getState() { - return open; - } - - private void setText(String text) { - DOM.setInnerText(nodeCaptionSpan, text); - } - - private void renderChildNodes(Iterator i) { - childNodeContainer.clear(); - childNodeContainer.setVisible(true); - while (i.hasNext()) { - final UIDL childUidl = (UIDL) i.next(); - // actions are in bit weird place, don't mix them with children, - // but current node's actions - if ("actions".equals(childUidl.getTag())) { - updateActionMap(childUidl); - continue; - } - final TreeNode childTree = new TreeNode(); - childNodeContainer.add(childTree); - childTree.updateFromUIDL(childUidl, client); - } - childrenLoaded = true; - } - - public boolean isChildrenLoaded() { - return childrenLoaded; - } - - public Action[] getActions() { - if (actionKeys == null) { - return new Action[] {}; - } - final Action[] actions = new Action[actionKeys.length]; - for (int i = 0; i < actions.length; i++) { - final String actionKey = actionKeys[i]; - final TreeAction a = new TreeAction(this, String.valueOf(key), - actionKey); - a.setCaption(getActionCaption(actionKey)); - a.setIconUrl(getActionIcon(actionKey)); - actions[i] = a; - } - return actions; - } - - public ApplicationConnection getClient() { - return client; - } - - public String getPaintableId() { - return paintableId; - } - - /** - * Adds/removes IT Mill Toolkit specific style name. This method ought - * to be called only from ITree. - * - * @param selected - */ - protected void setSelected(boolean selected) { - // add style name to caption dom structure only, not to subtree - setStyleName(nodeCaptionDiv, "i-tree-node-selected", selected); - } - - protected boolean isSelected() { - return ITree.this.isSelected(this); - } - - public void showContextMenu(Event event) { - if (!readonly && !disabled) { - if (actionKeys != null) { - int left = event.getClientX(); - int top = event.getClientY(); - top += Window.getScrollTop(); - left += Window.getScrollLeft(); - client.getContextMenu().showAt(this, left, top); - } - event.cancelBubble(true); - event.preventDefault(); - } - } - - /* - * We need to fix the width of TreeNodes so that the float in - * ie6compatNode does not wrap (see ticket #1245) - */ - private void fixWidth() { - nodeCaptionDiv.getStyle().setProperty("styleFloat", "left"); - nodeCaptionDiv.getStyle().setProperty("display", "inline"); - nodeCaptionDiv.getStyle().setProperty("marginLeft", "0"); - final int captionWidth = ie6compatnode.getOffsetWidth() - + nodeCaptionDiv.getOffsetWidth(); - setWidth(captionWidth + "px"); - } - - @Override - public void onAttach() { - super.onAttach(); - if (BrowserInfo.get().isIE6()) { - fixWidth(); - } - } - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/ITwinColSelect.java b/src/com/vaadin/terminal/gwt/client/ui/ITwinColSelect.java deleted file mode 100644 index c29f520f8d..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/ITwinColSelect.java +++ /dev/null @@ -1,244 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.Iterator; -import java.util.Vector; - -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.ListBox; -import com.google.gwt.user.client.ui.Panel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.UIDL; - -public class ITwinColSelect extends IOptionGroupBase { - - private static final String CLASSNAME = "i-select-twincol"; - - private static final int VISIBLE_COUNT = 10; - - private static final int DEFAULT_COLUMN_COUNT = 10; - - private final ListBox options; - - private final ListBox selections; - - private final IButton add; - - private final IButton remove; - - private FlowPanel buttons; - - private Panel panel; - - private boolean widthSet = false; - - public ITwinColSelect() { - super(CLASSNAME); - options = new ListBox(); - options.addClickListener(this); - selections = new ListBox(); - selections.addClickListener(this); - options.setVisibleItemCount(VISIBLE_COUNT); - selections.setVisibleItemCount(VISIBLE_COUNT); - options.setStyleName(CLASSNAME + "-options"); - selections.setStyleName(CLASSNAME + "-selections"); - buttons = new FlowPanel(); - buttons.setStyleName(CLASSNAME + "-buttons"); - add = new IButton(); - add.setText(">>"); - add.addClickListener(this); - remove = new IButton(); - remove.setText("<<"); - remove.addClickListener(this); - panel = ((Panel) optionsContainer); - panel.add(options); - buttons.add(add); - final HTML br = new HTML(""); - br.setStyleName(CLASSNAME + "-deco"); - buttons.add(br); - buttons.add(remove); - panel.add(buttons); - panel.add(selections); - } - - @Override - protected void buildOptions(UIDL uidl) { - final boolean enabled = !isDisabled() && !isReadonly(); - options.setMultipleSelect(isMultiselect()); - selections.setMultipleSelect(isMultiselect()); - options.setEnabled(enabled); - selections.setEnabled(enabled); - add.setEnabled(enabled); - remove.setEnabled(enabled); - options.clear(); - selections.clear(); - for (final Iterator i = uidl.getChildIterator(); i.hasNext();) { - final UIDL optionUidl = (UIDL) i.next(); - if (optionUidl.hasAttribute("selected")) { - selections.addItem(optionUidl.getStringAttribute("caption"), - optionUidl.getStringAttribute("key")); - } else { - options.addItem(optionUidl.getStringAttribute("caption"), - optionUidl.getStringAttribute("key")); - } - } - - int cols = -1; - if (getColumns() > 0) { - cols = getColumns(); - } else if (!widthSet) { - cols = DEFAULT_COLUMN_COUNT; - } - - if (cols >= 0) { - options.setWidth(cols + "em"); - selections.setWidth(cols + "em"); - buttons.setWidth("3.5em"); - optionsContainer.setWidth((2 * cols + 4) + "em"); - } - if (getRows() > 0) { - options.setVisibleItemCount(getRows()); - selections.setVisibleItemCount(getRows()); - - } - - } - - @Override - protected Object[] getSelectedItems() { - final Vector selectedItemKeys = new Vector(); - for (int i = 0; i < selections.getItemCount(); i++) { - selectedItemKeys.add(selections.getValue(i)); - } - return selectedItemKeys.toArray(); - } - - private boolean[] getItemsToAdd() { - final boolean[] selectedIndexes = new boolean[options.getItemCount()]; - for (int i = 0; i < options.getItemCount(); i++) { - if (options.isItemSelected(i)) { - selectedIndexes[i] = true; - } else { - selectedIndexes[i] = false; - } - } - return selectedIndexes; - } - - private boolean[] getItemsToRemove() { - final boolean[] selectedIndexes = new boolean[selections.getItemCount()]; - for (int i = 0; i < selections.getItemCount(); i++) { - if (selections.isItemSelected(i)) { - selectedIndexes[i] = true; - } else { - selectedIndexes[i] = false; - } - } - return selectedIndexes; - } - - @Override - public void onClick(Widget sender) { - super.onClick(sender); - if (sender == add) { - final boolean[] sel = getItemsToAdd(); - for (int i = 0; i < sel.length; i++) { - if (sel[i]) { - final int optionIndex = i - - (sel.length - options.getItemCount()); - selectedKeys.add(options.getValue(optionIndex)); - - // Move selection to another column - final String text = options.getItemText(optionIndex); - final String value = options.getValue(optionIndex); - selections.addItem(text, value); - selections.setItemSelected(selections.getItemCount() - 1, - true); - options.removeItem(optionIndex); - } - } - client.updateVariable(id, "selected", selectedKeys.toArray(), - isImmediate()); - - } else if (sender == remove) { - final boolean[] sel = getItemsToRemove(); - for (int i = 0; i < sel.length; i++) { - if (sel[i]) { - final int selectionIndex = i - - (sel.length - selections.getItemCount()); - selectedKeys.remove(selections.getValue(selectionIndex)); - - // Move selection to another column - final String text = selections.getItemText(selectionIndex); - final String value = selections.getValue(selectionIndex); - options.addItem(text, value); - options.setItemSelected(options.getItemCount() - 1, true); - selections.removeItem(selectionIndex); - } - } - client.updateVariable(id, "selected", selectedKeys.toArray(), - isImmediate()); - } else if (sender == options) { - // unselect all in other list, to avoid mistakes (i.e wrong button) - final int c = selections.getItemCount(); - for (int i = 0; i < c; i++) { - selections.setItemSelected(i, false); - } - } else if (sender == selections) { - // unselect all in other list, to avoid mistakes (i.e wrong button) - final int c = options.getItemCount(); - for (int i = 0; i < c; i++) { - options.setItemSelected(i, false); - } - } - } - - @Override - public void setHeight(String height) { - super.setHeight(height); - if ("".equals(height)) { - options.setHeight(""); - selections.setHeight(""); - } else { - setFullHeightInternals(); - } - } - - private void setFullHeightInternals() { - options.setHeight("100%"); - selections.setHeight("100%"); - } - - @Override - public void setWidth(String width) { - super.setWidth(width); - if (!"".equals(width) && width != null) { - setRelativeInternalWidths(); - } - } - - private void setRelativeInternalWidths() { - DOM.setStyleAttribute(getElement(), "position", "relative"); - buttons.setWidth("15%"); - options.setWidth("42%"); - selections.setWidth("42%"); - widthSet = true; - } - - @Override - protected void setTabIndex(int tabIndex) { - options.setTabIndex(tabIndex); - selections.setTabIndex(tabIndex); - add.setTabIndex(tabIndex); - remove.setTabIndex(tabIndex); - } - - public void focus() { - options.setFocus(true); - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IUnknownComponent.java b/src/com/vaadin/terminal/gwt/client/ui/IUnknownComponent.java deleted file mode 100644 index 5715ab02c8..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IUnknownComponent.java +++ /dev/null @@ -1,40 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.Tree; -import com.google.gwt.user.client.ui.VerticalPanel; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; - -public class IUnknownComponent extends Composite implements Paintable { - - com.google.gwt.user.client.ui.Label caption = new com.google.gwt.user.client.ui.Label();; - Tree uidlTree = new Tree(); - - public IUnknownComponent() { - final VerticalPanel panel = new VerticalPanel(); - panel.add(caption); - panel.add(uidlTree); - initWidget(panel); - setStyleName("itmill-unknown"); - caption.setStyleName("itmill-unknown-caption"); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - if (client.updateComponent(this, uidl, false)) { - return; - } - setCaption("Client faced an unknown component type. Unrendered UIDL:"); - uidlTree.clear(); - uidlTree.addItem(uidl.dir()); - } - - public void setCaption(String c) { - caption.setText(c); - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IUpload.java b/src/com/vaadin/terminal/gwt/client/ui/IUpload.java deleted file mode 100644 index a03beed3e2..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IUpload.java +++ /dev/null @@ -1,152 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.ui.Button; -import com.google.gwt.user.client.ui.ClickListener; -import com.google.gwt.user.client.ui.FileUpload; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.FormHandler; -import com.google.gwt.user.client.ui.FormPanel; -import com.google.gwt.user.client.ui.FormSubmitCompleteEvent; -import com.google.gwt.user.client.ui.FormSubmitEvent; -import com.google.gwt.user.client.ui.Panel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; - -public class IUpload extends FormPanel implements Paintable, ClickListener, - FormHandler { - - public static final String CLASSNAME = "i-upload"; - - /** - * FileUpload component that opens native OS dialog to select file. - */ - FileUpload fu = new FileUpload(); - - Panel panel = new FlowPanel(); - - ApplicationConnection client; - - private String paintableId; - - /** - * Button that initiates uploading - */ - private final Button submitButton; - - /** - * When expecting big files, programmer may initiate some UI changes when - * uploading the file starts. Bit after submitting file we'll visit the - * server to check possible changes. - */ - private Timer t; - - /** - * some browsers tries to send form twice if submit is called in button - * click handler, some don't submit at all without it, so we need to track - * if form is already being submitted - */ - private boolean submitted = false; - - private boolean enabled = true; - - public IUpload() { - super(); - setEncoding(FormPanel.ENCODING_MULTIPART); - setMethod(FormPanel.METHOD_POST); - - setWidget(panel); - panel.add(fu); - submitButton = new Button(); - submitButton.addClickListener(this); - panel.add(submitButton); - - addFormHandler(this); - - setStyleName(CLASSNAME); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - if (client.updateComponent(this, uidl, true)) { - return; - } - this.client = client; - paintableId = uidl.getId(); - setAction(client.getAppUri()); - submitButton.setText(uidl.getStringAttribute("buttoncaption")); - fu.setName(paintableId + "_file"); - - if (uidl.hasAttribute("disabled") || uidl.hasAttribute("readonly")) { - disableUpload(); - } else if (uidl.getBooleanAttribute("state")) { - enableUploaod(); - } - - } - - public void onClick(Widget sender) { - submit(); - } - - public void onSubmit(FormSubmitEvent event) { - if (fu.getFilename().length() == 0 || submitted || !enabled) { - event.setCancelled(true); - ApplicationConnection - .getConsole() - .log( - "Submit cancelled (disabled, no file or already submitted)"); - return; - } - // flush possibly pending variable changes, so they will be handled - // before upload - client.sendPendingVariableChanges(); - - submitted = true; - ApplicationConnection.getConsole().log("Submitted form"); - - disableUpload(); - - /* - * Visit server a moment after upload has started to see possible - * changes from UploadStarted event. Will be cleared on complete. - */ - t = new Timer() { - @Override - public void run() { - client.sendPendingVariableChanges(); - } - }; - t.schedule(800); - } - - protected void disableUpload() { - submitButton.setEnabled(false); - fu.setVisible(false); - enabled = false; - } - - protected void enableUploaod() { - submitButton.setEnabled(true); - fu.setVisible(true); - enabled = true; - } - - public void onSubmitComplete(FormSubmitCompleteEvent event) { - if (client != null) { - if (t != null) { - t.cancel(); - } - ApplicationConnection.getConsole().log("Submit complete"); - client.sendPendingVariableChanges(); - } - submitted = false; - enableUploaod(); - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IUriFragmentUtility.java b/src/com/vaadin/terminal/gwt/client/ui/IUriFragmentUtility.java deleted file mode 100644 index d2237c5f60..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IUriFragmentUtility.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.vaadin.terminal.gwt.client.ui; - -import com.google.gwt.dom.client.Document; -import com.google.gwt.user.client.History; -import com.google.gwt.user.client.HistoryListener; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; - -/** - * Client side implementation for UriFragmentUtility. Uses GWT's History object - * as an implementation. - * - */ -public class IUriFragmentUtility extends Widget implements Paintable, - HistoryListener { - - private String fragment; - private ApplicationConnection client; - private String paintableId; - private boolean immediate; - - public IUriFragmentUtility() { - setElement(Document.get().createDivElement()); - if (BrowserInfo.get().isIE6()) { - getElement().getStyle().setProperty("overflow", "hidden"); - getElement().getStyle().setProperty("height", "0"); - } - History.addHistoryListener(this); - History.fireCurrentHistoryState(); - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - if (client.updateComponent(this, uidl, false)) { - return; - } - String uidlFragment = uidl.getStringVariable("fragment"); - immediate = uidl.getBooleanAttribute("immediate"); - if (this.client == null) { - // initial paint has some special logic - this.client = client; - paintableId = uidl.getId(); - if (!fragment.equals(uidlFragment)) { - // initial server side fragment (from link/bookmark/typed) does - // not equal the one on - // server, send initial fragment to server - History.fireCurrentHistoryState(); - } - } else { - if (uidlFragment != null && !uidlFragment.equals(fragment)) { - fragment = uidlFragment; - // normal fragment change from server, add new history item - History.newItem(uidlFragment, false); - } - } - } - - public void onHistoryChanged(String historyToken) { - fragment = historyToken; - if (client != null) { - client.updateVariable(paintableId, "fragment", fragment, immediate); - } - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IVerticalLayout.java b/src/com/vaadin/terminal/gwt/client/ui/IVerticalLayout.java deleted file mode 100644 index 9adeb8158e..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IVerticalLayout.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.vaadin.terminal.gwt.client.ui; - -public class IVerticalLayout extends IOrderedLayout { - - public static final String CLASSNAME = "i-verticallayout"; - - public IVerticalLayout() { - super(CLASSNAME, ORIENTATION_VERTICAL); - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IView.java b/src/com/vaadin/terminal/gwt/client/ui/IView.java deleted file mode 100644 index 2d8ce9a986..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IView.java +++ /dev/null @@ -1,575 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.Set; - -import com.google.gwt.dom.client.DivElement; -import com.google.gwt.dom.client.Document; -import com.google.gwt.user.client.Command; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.DeferredCommand; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.WindowCloseListener; -import com.google.gwt.user.client.WindowResizeListener; -import com.google.gwt.user.client.ui.HasFocus; -import com.google.gwt.user.client.ui.RootPanel; -import com.google.gwt.user.client.ui.SimplePanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.Container; -import com.vaadin.terminal.gwt.client.Focusable; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.RenderSpace; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; - -/** - * - */ -public class IView extends SimplePanel implements Container, - WindowResizeListener, WindowCloseListener { - - private static final String CLASSNAME = "i-view"; - - private String theme; - - private Paintable layout; - - private final LinkedHashSet subWindows = new LinkedHashSet(); - - private String id; - - private ShortcutActionHandler actionHandler; - - /** stored width for IE resize optimization */ - private int width; - - /** stored height for IE resize optimization */ - private int height; - - private ApplicationConnection connection; - - /** - * We are postponing resize process with IE. IE bugs with scrollbars in some - * situations, that causes false onWindowResized calls. With Timer we will - * give IE some time to decide if it really wants to keep current size - * (scrollbars). - */ - private Timer resizeTimer; - - private int scrollTop; - - private int scrollLeft; - - private boolean rendering; - - private boolean scrollable; - - private boolean immediate; - - public IView(String elementId) { - super(); - setStyleName(CLASSNAME); - - DOM.sinkEvents(getElement(), Event.ONKEYDOWN | Event.ONSCROLL); - - // iview is focused when created so element needs tabIndex - // 1 due 0 is at the end of natural tabbing order - DOM.setElementProperty(getElement(), "tabIndex", "1"); - - RootPanel root = RootPanel.get(elementId); - root.add(this); - root.removeStyleName("i-app-loading"); - - BrowserInfo browser = BrowserInfo.get(); - - // set focus to iview element by default to listen possible keyboard - // shortcuts - if (browser.isOpera() || browser.isSafari() - && browser.getWebkitVersion() < 526) { - // old webkits don't support focusing div elements - Element fElem = DOM.createInputCheck(); - DOM.setStyleAttribute(fElem, "margin", "0"); - DOM.setStyleAttribute(fElem, "padding", "0"); - DOM.setStyleAttribute(fElem, "border", "0"); - DOM.setStyleAttribute(fElem, "outline", "0"); - DOM.setStyleAttribute(fElem, "width", "1px"); - DOM.setStyleAttribute(fElem, "height", "1px"); - DOM.setStyleAttribute(fElem, "position", "absolute"); - DOM.setStyleAttribute(fElem, "opacity", "0.1"); - DOM.appendChild(getElement(), fElem); - focus(fElem); - } else { - focus(getElement()); - } - - } - - private static native void focus(Element el) - /*-{ - try { - el.focus(); - } catch (e) { - - } - }-*/; - - public String getTheme() { - return theme; - } - - /** - * Used to reload host page on theme changes. - */ - private static native void reloadHostPage() - /*-{ - $wnd.location.reload(); - }-*/; - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - rendering = true; - - id = uidl.getId(); - boolean firstPaint = connection == null; - connection = client; - - immediate = uidl.hasAttribute("immediate"); - - String newTheme = uidl.getStringAttribute("theme"); - if (theme != null && !newTheme.equals(theme)) { - // Complete page refresh is needed due css can affect layout - // calculations etc - reloadHostPage(); - } else { - theme = newTheme; - } - if (uidl.hasAttribute("style")) { - addStyleName(uidl.getStringAttribute("style")); - } - - if (uidl.hasAttribute("name")) { - client.setWindowName(uidl.getStringAttribute("name")); - } - - com.google.gwt.user.client.Window.setTitle(uidl - .getStringAttribute("caption")); - - // Process children - int childIndex = 0; - - // Open URL:s - boolean isClosed = false; // was this window closed? - while (childIndex < uidl.getChildCount() - && "open".equals(uidl.getChildUIDL(childIndex).getTag())) { - final UIDL open = uidl.getChildUIDL(childIndex); - final String url = open.getStringAttribute("src"); - final String target = open.getStringAttribute("name"); - if (target == null) { - // This window is closing. Send close event before - // going to the new url - isClosed = true; - onWindowClosed(); - goTo(url); - } else { - String options; - if (open.hasAttribute("border")) { - if (open.getStringAttribute("border").equals("minimal")) { - options = "menubar=yes,location=no,status=no"; - } else { - options = "menubar=no,location=no,status=no"; - } - - } else { - options = "resizable=yes,menubar=yes,toolbar=yes,directories=yes,location=yes,scrollbars=yes,status=yes"; - } - - if (open.hasAttribute("width")) { - int w = open.getIntAttribute("width"); - options += ",width=" + w; - } - if (open.hasAttribute("height")) { - int h = open.getIntAttribute("height"); - options += ",height=" + h; - } - - Window.open(url, target, options); - } - childIndex++; - } - if (isClosed) { - // don't render the content - rendering = false; - return; - } - - // Draw this application level window - UIDL childUidl = uidl.getChildUIDL(childIndex); - final Paintable lo = client.getPaintable(childUidl); - - if (layout != null) { - if (layout != lo) { - // remove old - client.unregisterPaintable(layout); - // add new - setWidget((Widget) lo); - layout = lo; - } - } else { - setWidget((Widget) lo); - layout = lo; - } - - layout.updateFromUIDL(childUidl, client); - - // Update subwindows - final HashSet removedSubWindows = new HashSet( - subWindows); - - // Open new windows - while ((childUidl = uidl.getChildUIDL(childIndex++)) != null) { - if ("window".equals(childUidl.getTag())) { - final Paintable w = client.getPaintable(childUidl); - if (subWindows.contains(w)) { - removedSubWindows.remove(w); - } else { - subWindows.add((IWindow) w); - } - w.updateFromUIDL(childUidl, client); - } else if ("actions".equals(childUidl.getTag())) { - if (actionHandler == null) { - actionHandler = new ShortcutActionHandler(id, client); - } - actionHandler.updateActionMap(childUidl); - } else if (childUidl.getTag().equals("notifications")) { - for (final Iterator it = childUidl.getChildIterator(); it - .hasNext();) { - final UIDL notification = (UIDL) it.next(); - String html = ""; - if (notification.hasAttribute("icon")) { - final String parsedUri = client - .translateToolkitUri(notification - .getStringAttribute("icon")); - html += ""; - } - if (notification.hasAttribute("caption")) { - html += "

" - + notification.getStringAttribute("caption") - + "

"; - } - if (notification.hasAttribute("message")) { - html += "

" - + notification.getStringAttribute("message") - + "

"; - } - - final String style = notification.hasAttribute("style") ? notification - .getStringAttribute("style") - : null; - final int position = notification - .getIntAttribute("position"); - final int delay = notification.getIntAttribute("delay"); - new INotification(delay).show(html, position, style); - } - } - } - - // Close old windows - for (final Iterator rem = removedSubWindows.iterator(); rem - .hasNext();) { - final IWindow w = rem.next(); - client.unregisterPaintable(w); - subWindows.remove(w); - w.hide(); - } - - if (uidl.hasAttribute("focused")) { - final String focusPid = uidl.getStringAttribute("focused"); - // set focused component when render phase is finished - DeferredCommand.addCommand(new Command() { - public void execute() { - final Paintable toBeFocused = connection - .getPaintable(focusPid); - - /* - * Two types of Widgets can be focused, either implementing - * GWT HasFocus of a thinner Toolkit specific Focusable - * interface. - */ - if (toBeFocused instanceof HasFocus) { - final HasFocus toBeFocusedWidget = (HasFocus) toBeFocused; - toBeFocusedWidget.setFocus(true); - } else if (toBeFocused instanceof Focusable) { - ((Focusable) toBeFocused).focus(); - } else { - ApplicationConnection.getConsole().log( - "Could not focus component"); - } - } - }); - } - - // Add window listeners on first paint, to prevent premature - // variablechanges - if (firstPaint) { - Window.addWindowCloseListener(this); - Window.addWindowResizeListener(this); - } - - onWindowResized(Window.getClientWidth(), Window.getClientHeight()); - - if (BrowserInfo.get().isSafari()) { - Util.runWebkitOverflowAutoFix(getElement()); - } - - // finally set scroll position from UIDL - if (uidl.hasVariable("scrollTop")) { - scrollable = true; - scrollTop = uidl.getIntVariable("scrollTop"); - DOM.setElementPropertyInt(getElement(), "scrollTop", scrollTop); - scrollLeft = uidl.getIntVariable("scrollLeft"); - DOM.setElementPropertyInt(getElement(), "scrollLeft", scrollLeft); - } else { - scrollable = false; - } - - rendering = false; - } - - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - int type = DOM.eventGetType(event); - if (type == Event.ONKEYDOWN && actionHandler != null) { - actionHandler.handleKeyboardEvent(event); - return; - } else if (scrollable && type == Event.ONSCROLL) { - updateScrollPosition(); - } - } - - /** - * Updates scroll position from DOM and saves variables to server. - */ - private void updateScrollPosition() { - int oldTop = scrollTop; - int oldLeft = scrollLeft; - scrollTop = DOM.getElementPropertyInt(getElement(), "scrollTop"); - scrollLeft = DOM.getElementPropertyInt(getElement(), "scrollLeft"); - if (connection != null && !rendering) { - if (oldTop != scrollTop) { - connection.updateVariable(id, "scrollTop", scrollTop, false); - } - if (oldLeft != scrollLeft) { - connection.updateVariable(id, "scrollLeft", scrollLeft, false); - } - } - } - - public void onWindowResized(int width, int height) { - if (BrowserInfo.get().isIE()) { - /* - * IE will give us some false resized events due bugs with - * scrollbars. Postponing layout phase to see if size was really - * changed. - */ - if (resizeTimer == null) { - resizeTimer = new Timer() { - @Override - public void run() { - boolean changed = false; - if (IView.this.width != getOffsetWidth()) { - IView.this.width = getOffsetWidth(); - changed = true; - ApplicationConnection.getConsole().log( - "window w" + IView.this.width); - } - if (IView.this.height != getOffsetHeight()) { - IView.this.height = getOffsetHeight(); - changed = true; - ApplicationConnection.getConsole().log( - "window h" + IView.this.height); - } - if (changed) { - ApplicationConnection - .getConsole() - .log( - "Running layout functions due window resize"); - connection.runDescendentsLayout(IView.this); - - sendClientResized(); - } - } - }; - } else { - resizeTimer.cancel(); - } - resizeTimer.schedule(200); - } else { - if (width == IView.this.width && height == IView.this.height) { - // No point in doing resize operations if window size has not - // changed - return; - } - - IView.this.width = Window.getClientWidth(); - IView.this.height = Window.getClientHeight(); - - ApplicationConnection.getConsole().log( - "Running layout functions due window resize"); - - connection.runDescendentsLayout(this); - Util.runWebkitOverflowAutoFix(getElement()); - - sendClientResized(); - } - - } - - /** - * Send new dimensions to the server. - */ - private void sendClientResized() { - connection.updateVariable(id, "height", height, false); - connection.updateVariable(id, "width", width, immediate); - } - - public native static void goTo(String url) - /*-{ - $wnd.location = url; - }-*/; - - public void onWindowClosed() { - // Change focus on this window in order to ensure that all state is - // collected from textfields - ITextField.flushChangesFromFocusedTextField(); - - // Send the closing state to server - connection.updateVariable(id, "close", true, false); - connection.sendPendingVariableChangesSync(); - } - - public String onWindowClosing() { - return null; - } - - private final RenderSpace myRenderSpace = new RenderSpace() { - private int excessHeight = -1; - private int excessWidth = -1; - - @Override - public int getHeight() { - return getElement().getOffsetHeight() - getExcessHeight(); - } - - private int getExcessHeight() { - if (excessHeight < 0) { - detectExcessSize(); - } - return excessHeight; - } - - private void detectExcessSize() { - // TODO define that iview cannot be themed and decorations should - // get to parent element, then get rid of this expensive and error - // prone function - final String overflow = getElement().getStyle().getProperty( - "overflow"); - getElement().getStyle().setProperty("overflow", "hidden"); - if (BrowserInfo.get().isIE() - && getElement().getPropertyInt("clientWidth") == 0) { - // can't detect possibly themed border/padding width in some - // situations (with some layout configurations), use empty div - // to measure width properly - DivElement div = Document.get().createDivElement(); - div.setInnerHTML(" "); - div.getStyle().setProperty("overflow", "hidden"); - div.getStyle().setProperty("height", "1px"); - getElement().appendChild(div); - excessWidth = getElement().getOffsetWidth() - - div.getOffsetWidth(); - getElement().removeChild(div); - } else { - excessWidth = getElement().getOffsetWidth() - - getElement().getPropertyInt("clientWidth"); - } - excessHeight = getElement().getOffsetHeight() - - getElement().getPropertyInt("clientHeight"); - - getElement().getStyle().setProperty("overflow", overflow); - } - - @Override - public int getWidth() { - return getElement().getOffsetWidth() - getExcessWidth(); - } - - private int getExcessWidth() { - if (excessWidth < 0) { - detectExcessSize(); - } - return excessWidth; - } - - @Override - public int getScrollbarSize() { - return Util.getNativeScrollbarSize(); - } - }; - - public RenderSpace getAllocatedSpace(Widget child) { - return myRenderSpace; - } - - public boolean hasChildComponent(Widget component) { - return (component != null && component == layout); - } - - public void replaceChildComponent(Widget oldComponent, Widget newComponent) { - // TODO This is untested as no layouts require this - if (oldComponent != layout) { - return; - } - - setWidget(newComponent); - layout = (Paintable) newComponent; - } - - public boolean requestLayout(Set child) { - /* - * Can never propagate further and we do not want need to re-layout the - * layout which has caused this request. - */ - return true; - - } - - public void updateCaption(Paintable component, UIDL uidl) { - // NOP Subwindows never draw caption for their first child (layout) - } - - /** - * Return an iterator for current subwindows. This method is meant for - * testing purposes only. - * - * @return - */ - public ArrayList getSubWindowList() { - ArrayList windows = new ArrayList(subWindows.size()); - for (IWindow widget : subWindows) { - windows.add(widget); - } - return windows; - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/IWindow.java b/src/com/vaadin/terminal/gwt/client/ui/IWindow.java deleted file mode 100644 index eaa1c99da1..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/IWindow.java +++ /dev/null @@ -1,1001 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui; - -import java.util.Iterator; -import java.util.Set; -import java.util.Vector; - -import com.google.gwt.user.client.Command; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.DeferredCommand; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.ui.Frame; -import com.google.gwt.user.client.ui.HasWidgets; -import com.google.gwt.user.client.ui.RootPanel; -import com.google.gwt.user.client.ui.ScrollListener; -import com.google.gwt.user.client.ui.ScrollPanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.Container; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.RenderSpace; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; - -/** - * "Sub window" component. - * - * TODO update position / scroll position / size to client - * - * @author IT Mill Ltd - */ -public class IWindow extends IToolkitOverlay implements Container, - ScrollListener { - - private static final int MIN_HEIGHT = 100; - - private static final int MIN_WIDTH = 150; - - private static Vector windowOrder = new Vector(); - - public static final String CLASSNAME = "i-window"; - - /** - * Pixels used by inner borders and paddings horizontally (calculated only - * once) - */ - private int borderWidth = -1; - - /** - * Pixels used by inner borders and paddings vertically (calculated only - * once) - */ - private int borderHeight = -1; - - private static final int STACKING_OFFSET_PIXELS = 15; - - public static final int Z_INDEX = 10000; - - private Paintable layout; - - private Element contents; - - private Element header; - - private Element footer; - - private Element resizeBox; - - private final ScrollPanel contentPanel = new ScrollPanel(); - - private boolean dragging; - - private int startX; - - private int startY; - - private int origX; - - private int origY; - - private boolean resizing; - - private int origW; - - private int origH; - - private Element closeBox; - - protected ApplicationConnection client; - - private String id; - - ShortcutActionHandler shortcutHandler; - - /** Last known positionx read from UIDL or updated to application connection */ - private int uidlPositionX = -1; - - /** Last known positiony read from UIDL or updated to application connection */ - private int uidlPositionY = -1; - - private boolean vaadinModality = false; - - private boolean resizable = true; - - private Element modalityCurtain; - private Element draggingCurtain; - - private Element headerText; - - private boolean readonly; - - boolean dynamicWidth = false; - boolean dynamicHeight = false; - boolean layoutRelativeWidth = false; - boolean layoutRelativeHeight = false; - - // If centered (via UIDL), the window should stay in the centered -mode - // until a position is received from the server, or the user moves or - // resizes the window. - boolean centered = false; - - private RenderSpace renderSpace = new RenderSpace(MIN_WIDTH, MIN_HEIGHT, - true); - - private String width; - - private String height; - - private boolean immediate; - - public IWindow() { - super(false, false, true); // no autohide, not modal, shadow - // Different style of shadow for windows - setShadowStyle("window"); - - final int order = windowOrder.size(); - setWindowOrder(order); - windowOrder.add(this); - constructDOM(); - setPopupPosition(order * STACKING_OFFSET_PIXELS, order - * STACKING_OFFSET_PIXELS); - contentPanel.addScrollListener(this); - } - - private void bringToFront() { - int curIndex = windowOrder.indexOf(this); - if (curIndex + 1 < windowOrder.size()) { - windowOrder.remove(this); - windowOrder.add(this); - for (; curIndex < windowOrder.size(); curIndex++) { - windowOrder.get(curIndex).setWindowOrder(curIndex); - } - } - } - - /** - * Returns true if window is the topmost window - * - * @return - */ - private boolean isActive() { - return windowOrder.lastElement().equals(this); - } - - public void setWindowOrder(int order) { - setZIndex(order + Z_INDEX); - } - - @Override - protected void setZIndex(int zIndex) { - super.setZIndex(zIndex); - if (vaadinModality) { - DOM.setStyleAttribute(modalityCurtain, "zIndex", "" + zIndex); - } - } - - protected void constructDOM() { - setStyleName(CLASSNAME); - - header = DOM.createDiv(); - DOM.setElementProperty(header, "className", CLASSNAME + "-outerheader"); - headerText = DOM.createDiv(); - DOM.setElementProperty(headerText, "className", CLASSNAME + "-header"); - contents = DOM.createDiv(); - DOM.setElementProperty(contents, "className", CLASSNAME + "-contents"); - footer = DOM.createDiv(); - DOM.setElementProperty(footer, "className", CLASSNAME + "-footer"); - resizeBox = DOM.createDiv(); - DOM - .setElementProperty(resizeBox, "className", CLASSNAME - + "-resizebox"); - closeBox = DOM.createDiv(); - DOM.setElementProperty(closeBox, "className", CLASSNAME + "-closebox"); - DOM.appendChild(footer, resizeBox); - - DOM.sinkEvents(getElement(), Event.ONLOSECAPTURE); - DOM.sinkEvents(closeBox, Event.ONCLICK); - DOM.sinkEvents(contents, Event.ONCLICK); - - final Element wrapper = DOM.createDiv(); - DOM.setElementProperty(wrapper, "className", CLASSNAME + "-wrap"); - - final Element wrapper2 = DOM.createDiv(); - DOM.setElementProperty(wrapper2, "className", CLASSNAME + "-wrap2"); - - DOM.sinkEvents(wrapper, Event.ONKEYDOWN); - - DOM.appendChild(wrapper2, closeBox); - DOM.appendChild(wrapper2, header); - DOM.appendChild(header, headerText); - DOM.appendChild(wrapper2, contents); - DOM.appendChild(wrapper2, footer); - DOM.appendChild(wrapper, wrapper2); - DOM.appendChild(super.getContainerElement(), wrapper); - - sinkEvents(Event.MOUSEEVENTS); - - setWidget(contentPanel); - - } - - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - id = uidl.getId(); - this.client = client; - - // Workaround needed for Testing Tools (GWT generates window DOM - // slightly different in different browsers). - DOM.setElementProperty(closeBox, "id", id + "_window_close"); - - if (uidl.hasAttribute("invisible")) { - hide(); - return; - } - - if (!uidl.hasAttribute("cached")) { - if (uidl.getBooleanAttribute("modal") != vaadinModality) { - setVaadinModality(!vaadinModality); - } - if (!isAttached()) { - show(); - } - } - - if (client.updateComponent(this, uidl, false)) { - return; - } - - immediate = uidl.hasAttribute("immediate"); - - if (uidl.getBooleanAttribute("resizable") != resizable) { - setResizable(!resizable); - } - - if (isReadOnly() != uidl.getBooleanAttribute("readonly")) { - setReadOnly(!isReadOnly()); - } - - // Initialize the position form UIDL - try { - final int positionx = uidl.getIntVariable("positionx"); - final int positiony = uidl.getIntVariable("positiony"); - if (positionx >= 0 && positiony >= 0) { - setPopupPosition(positionx, positiony); - } - } catch (final IllegalArgumentException e) { - // Silently ignored as positionx and positiony are not required - // parameters - } - - if (uidl.hasAttribute("caption")) { - setCaption(uidl.getStringAttribute("caption"), uidl - .getStringAttribute("icon")); - } - - boolean showingUrl = false; - int childIndex = 0; - UIDL childUidl = uidl.getChildUIDL(childIndex++); - while ("open".equals(childUidl.getTag())) { - // TODO multiple opens with the same target will in practice just - // open the last one - should we fix that somehow? - final String parsedUri = client.translateToolkitUri(childUidl - .getStringAttribute("src")); - if (!childUidl.hasAttribute("name")) { - final Frame frame = new Frame(); - DOM.setStyleAttribute(frame.getElement(), "width", "100%"); - DOM.setStyleAttribute(frame.getElement(), "height", "100%"); - DOM.setStyleAttribute(frame.getElement(), "border", "0px"); - frame.setUrl(parsedUri); - contentPanel.setWidget(frame); - showingUrl = true; - } else { - final String target = childUidl.getStringAttribute("name"); - Window.open(parsedUri, target, ""); - } - childUidl = uidl.getChildUIDL(childIndex++); - } - - final Paintable lo = client.getPaintable(childUidl); - if (layout != null) { - if (layout != lo) { - // remove old - client.unregisterPaintable(layout); - contentPanel.remove((Widget) layout); - // add new - if (!showingUrl) { - contentPanel.setWidget((Widget) lo); - } - layout = lo; - } - } else if (!showingUrl) { - contentPanel.setWidget((Widget) lo); - layout = lo; - } - - dynamicWidth = !uidl.hasAttribute("width"); - dynamicHeight = !uidl.hasAttribute("height"); - - layoutRelativeWidth = uidl.hasAttribute("layoutRelativeWidth"); - layoutRelativeHeight = uidl.hasAttribute("layoutRelativeHeight"); - - if (dynamicWidth && layoutRelativeWidth) { - /* - * Relative layout width, fix window width before rendering (width - * according to caption) - */ - setNaturalWidth(); - } - - layout.updateFromUIDL(childUidl, client); - if (!dynamicHeight && layoutRelativeWidth) { - /* - * Relative layout width, and fixed height. Must update the size to - * be able to take scrollbars into account (layout gets narrower - * space if it is higher than the window) -> only vertical scrollbar - */ - client.runDescendentsLayout(this); - } - - /* - * No explicit width is set and the layout does not have relative width - * so fix the size according to the layout. - */ - if (dynamicWidth && !layoutRelativeWidth) { - setNaturalWidth(); - } - - if (dynamicHeight && layoutRelativeHeight) { - // Prevent resizing until height has been fixed - resizable = false; - } - - // we may have actions and notifications - if (uidl.getChildCount() > 1) { - final int cnt = uidl.getChildCount(); - for (int i = 1; i < cnt; i++) { - childUidl = uidl.getChildUIDL(i); - if (childUidl.getTag().equals("actions")) { - if (shortcutHandler == null) { - shortcutHandler = new ShortcutActionHandler(id, client); - } - shortcutHandler.updateActionMap(childUidl); - } else if (childUidl.getTag().equals("notifications")) { - // TODO needed? move -> - for (final Iterator it = childUidl.getChildIterator(); it - .hasNext();) { - final UIDL notification = (UIDL) it.next(); - String html = ""; - if (notification.hasAttribute("icon")) { - final String parsedUri = client - .translateToolkitUri(notification - .getStringAttribute("icon")); - html += ""; - } - if (notification.hasAttribute("caption")) { - html += "

" - + notification - .getStringAttribute("caption") - + "

"; - } - if (notification.hasAttribute("message")) { - html += "

" - + notification - .getStringAttribute("message") - + "

"; - } - - final String style = notification.hasAttribute("style") ? notification - .getStringAttribute("style") - : null; - final int position = notification - .getIntAttribute("position"); - final int delay = notification.getIntAttribute("delay"); - new INotification(delay).show(html, position, style); - } - } - } - - } - - // setting scrollposition must happen after children is rendered - contentPanel.setScrollPosition(uidl.getIntVariable("scrollTop")); - contentPanel.setHorizontalScrollPosition(uidl - .getIntVariable("scrollLeft")); - - // Center this window on screen if requested - // This has to be here because we might not know the content size before - // everything is painted into the window - if (uidl.getBooleanAttribute("center")) { - // mark as centered - this is unset on move/resize - centered = true; - center(); - } else { - // don't try to center the window anymore - centered = false; - } - - updateShadowSizeAndPosition(); - - // ensure window is not larger than browser window - if (getOffsetWidth() > Window.getClientWidth()) { - setWidth(Window.getClientWidth() + "px"); - } - if (getOffsetHeight() > Window.getClientHeight()) { - setHeight(Window.getClientHeight() + "px"); - } - - if (dynamicHeight && layoutRelativeHeight) { - /* - * Window height is undefined, layout is 100% high so the layout - * should define the initial window height but on resize the layout - * should be as high as the window. We fix the height to deal with - * this. - */ - - int h = contents.getOffsetHeight() + getExtraHeight(); - int w = contents.getOffsetWidth(); - - client.updateVariable(id, "height", h, false); - client.updateVariable(id, "width", w, true); - } - - } - - private void setNaturalWidth() { - /* - * For some reason IE6 has title DIV set to width 100% which messes this - * up. Also IE6 has a 0 wide element so we use the container element. - */ - int naturalWidth; - if (BrowserInfo.get().isIE6()) { - String headerW = headerText.getStyle().getProperty("width"); - headerText.getStyle().setProperty("width", "auto"); - naturalWidth = getElement().getOffsetWidth(); - headerText.getStyle().setProperty("width", headerW); - } else { - // use max(layout width, window width) - // i.e layout content width or caption width - int lowidth = contentPanel.getElement().getScrollWidth() - + borderWidth; // layout does not know about border - int elwidth = getElement().getOffsetWidth(); - naturalWidth = (lowidth > elwidth ? lowidth : elwidth); - } - - setWidth(naturalWidth + "px"); - } - - private void setReadOnly(boolean readonly) { - this.readonly = readonly; - if (readonly) { - DOM.setStyleAttribute(closeBox, "display", "none"); - } else { - DOM.setStyleAttribute(closeBox, "display", ""); - } - } - - private boolean isReadOnly() { - return readonly; - } - - @Override - public void show() { - if (vaadinModality) { - showModalityCurtain(); - } - super.show(); - - setFF2CaretFixEnabled(true); - fixFF3OverflowBug(); - } - - /** Disable overflow auto with FF3 to fix #1837. */ - private void fixFF3OverflowBug() { - if (BrowserInfo.get().isFF3()) { - DeferredCommand.addCommand(new Command() { - public void execute() { - DOM.setStyleAttribute(getElement(), "overflow", ""); - } - }); - } - } - - /** - * Fix "missing cursor" browser bug workaround for FF2 in Windows and Linux. - * - * Calling this method has no effect on other browsers than the ones based - * on Gecko 1.8 - * - * @param enable - */ - private void setFF2CaretFixEnabled(boolean enable) { - if (BrowserInfo.get().isFF2()) { - if (enable) { - DeferredCommand.addCommand(new Command() { - public void execute() { - DOM.setStyleAttribute(getElement(), "overflow", "auto"); - } - }); - } else { - DOM.setStyleAttribute(getElement(), "overflow", ""); - } - } - } - - @Override - public void hide() { - if (vaadinModality) { - hideModalityCurtain(); - } - super.hide(); - } - - private void setVaadinModality(boolean modality) { - vaadinModality = modality; - if (vaadinModality) { - modalityCurtain = DOM.createDiv(); - DOM.setElementProperty(modalityCurtain, "className", CLASSNAME - + "-modalitycurtain"); - if (isAttached()) { - showModalityCurtain(); - bringToFront(); - } else { - DeferredCommand.addCommand(new Command() { - public void execute() { - // vaadinModality window must on top of others - bringToFront(); - } - }); - } - } else { - if (modalityCurtain != null) { - if (isAttached()) { - hideModalityCurtain(); - } - modalityCurtain = null; - } - } - } - - private void showModalityCurtain() { - if (BrowserInfo.get().isFF2()) { - DOM.setStyleAttribute(modalityCurtain, "height", DOM - .getElementPropertyInt(RootPanel.getBodyElement(), - "offsetHeight") - + "px"); - DOM.setStyleAttribute(modalityCurtain, "position", "absolute"); - } - DOM.setStyleAttribute(modalityCurtain, "zIndex", "" - + (windowOrder.indexOf(this) + Z_INDEX)); - DOM.appendChild(RootPanel.getBodyElement(), modalityCurtain); - } - - private void hideModalityCurtain() { - DOM.removeChild(RootPanel.getBodyElement(), modalityCurtain); - } - - /* - * Shows (or hides) an empty div on top of all other content; used when - * resizing or moving, so that iframes (etc) do not steal event. - */ - private void showDraggingCurtain(boolean show) { - if (show && draggingCurtain == null) { - - setFF2CaretFixEnabled(false); // makes FF2 slow - - draggingCurtain = DOM.createDiv(); - DOM.setStyleAttribute(draggingCurtain, "position", "absolute"); - DOM.setStyleAttribute(draggingCurtain, "top", "0px"); - DOM.setStyleAttribute(draggingCurtain, "left", "0px"); - DOM.setStyleAttribute(draggingCurtain, "width", "100%"); - DOM.setStyleAttribute(draggingCurtain, "height", "100%"); - DOM.setStyleAttribute(draggingCurtain, "zIndex", "" - + IToolkitOverlay.Z_INDEX); - - DOM.appendChild(RootPanel.getBodyElement(), draggingCurtain); - } else if (!show && draggingCurtain != null) { - - setFF2CaretFixEnabled(true); // makes FF2 slow - - DOM.removeChild(RootPanel.getBodyElement(), draggingCurtain); - draggingCurtain = null; - } - - } - - private void setResizable(boolean resizability) { - resizable = resizability; - if (resizability) { - DOM.setElementProperty(resizeBox, "className", CLASSNAME - + "-resizebox"); - } else { - DOM.setElementProperty(resizeBox, "className", CLASSNAME - + "-resizebox " + CLASSNAME + "-resizebox-disabled"); - } - } - - @Override - public void setPopupPosition(int left, int top) { - super.setPopupPosition(left, top); - if (left != uidlPositionX && client != null) { - client.updateVariable(id, "positionx", left, false); - uidlPositionX = left; - } - if (top != uidlPositionY && client != null) { - client.updateVariable(id, "positiony", top, false); - uidlPositionY = top; - } - } - - public void setCaption(String c) { - setCaption(c, null); - } - - public void setCaption(String c, String icon) { - String html = Util.escapeHTML(c); - if (icon != null) { - icon = client.translateToolkitUri(icon); - html = "" + html; - } - DOM.setInnerHTML(headerText, html); - } - - @Override - protected Element getContainerElement() { - // in GWT 1.5 this method is used in PopupPanel constructor - if (contents == null) { - return super.getContainerElement(); - } - return contents; - } - - @Override - public void onBrowserEvent(final Event event) { - if (event != null) { - final int type = event.getTypeInt(); - - if (type == Event.ONKEYDOWN && shortcutHandler != null) { - shortcutHandler.handleKeyboardEvent(event); - return; - } - - final Element target = DOM.eventGetTarget(event); - - // Handle window caption tooltips - if (client != null && DOM.isOrHasChild(header, target)) { - client.handleTooltipEvent(event, this); - } - - if (resizing || resizeBox == target) { - onResizeEvent(event); - event.cancelBubble(true); - } else if (target == closeBox) { - if (type == Event.ONCLICK) { - onCloseClick(); - event.cancelBubble(true); - } - } else if (dragging || !DOM.isOrHasChild(contents, target)) { - onDragEvent(event); - event.cancelBubble(true); - } else if (type == Event.ONCLICK) { - // clicked inside window, ensure to be on top - if (!isActive()) { - bringToFront(); - } - } - } - } - - private void onCloseClick() { - client.updateVariable(id, "close", true, true); - } - - private void onResizeEvent(Event event) { - if (resizable) { - switch (event.getTypeInt()) { - case Event.ONMOUSEDOWN: - if (!isActive()) { - bringToFront(); - } - showDraggingCurtain(true); - if (BrowserInfo.get().isIE()) { - DOM.setStyleAttribute(resizeBox, "visibility", "hidden"); - } - resizing = true; - startX = event.getScreenX(); - startY = event.getScreenY(); - origW = getElement().getOffsetWidth(); - origH = getElement().getOffsetHeight(); - DOM.setCapture(getElement()); - event.preventDefault(); - break; - case Event.ONMOUSEUP: - showDraggingCurtain(false); - if (BrowserInfo.get().isIE()) { - DOM.setStyleAttribute(resizeBox, "visibility", ""); - } - resizing = false; - DOM.releaseCapture(getElement()); - setSize(event, true); - break; - case Event.ONLOSECAPTURE: - showDraggingCurtain(false); - if (BrowserInfo.get().isIE()) { - DOM.setStyleAttribute(resizeBox, "visibility", ""); - } - resizing = false; - case Event.ONMOUSEMOVE: - if (resizing) { - centered = false; - setSize(event, false); - event.preventDefault(); - } - break; - default: - event.preventDefault(); - break; - } - } - } - - private void setSize(Event event, boolean updateVariables) { - int w = event.getScreenX() - startX + origW; - if (w < MIN_WIDTH + borderWidth) { - w = MIN_WIDTH + borderWidth; - } - - int h = event.getScreenY() - startY + origH; - if (h < MIN_HEIGHT + getExtraHeight()) { - h = MIN_HEIGHT + getExtraHeight(); - } - - setWidth(w + "px"); - setHeight(h + "px"); - - if (updateVariables) { - // sending width back always as pixels, no need for unit - client.updateVariable(id, "width", w, false); - client.updateVariable(id, "height", h, immediate); - } - - // Update child widget dimensions - if (client != null) { - client.handleComponentRelativeSize((Widget) layout); - client.runDescendentsLayout((HasWidgets) layout); - } - - Util.runWebkitOverflowAutoFix(contentPanel.getElement()); - } - - @Override - /* - * Width is set to the out-most element (i-window). - * - * This function should never be called with percentage values (it will - * throw an exception) - */ - public void setWidth(String width) { - this.width = width; - if (!isAttached()) { - return; - } - if (width != null && !"".equals(width)) { - int pixelWidth; - // Convert non-pixel values to pixels - if (width.indexOf("px") < 0) { - DOM.setStyleAttribute(getElement(), "width", width); - pixelWidth = getElement().getOffsetWidth(); - width = pixelWidth + "px"; - } - if (BrowserInfo.get().isIE6()) { - getElement().getStyle().setProperty("overflow", "hidden"); - } - getElement().getStyle().setProperty("width", width); - - pixelWidth = getElement().getOffsetWidth() - borderWidth; - if (pixelWidth < MIN_WIDTH) { - pixelWidth = MIN_WIDTH; - int rootWidth = pixelWidth + borderWidth; - DOM.setStyleAttribute(getElement(), "width", rootWidth + "px"); - } - - renderSpace.setWidth(pixelWidth); - - // IE6 needs the actual inner content width on the content element, - // otherwise it won't wrap the content properly (no scrollbars - // appear, content flows out of window) - if (BrowserInfo.get().isIE6()) { - DOM.setStyleAttribute(contentPanel.getElement(), "width", - pixelWidth + "px"); - } - updateShadowSizeAndPosition(); - } - } - - @Override - /* - * Height is set to the out-most element (i-window). - * - * This function should never be called with percentage values (it will - * throw an exception) - */ - public void setHeight(String height) { - this.height = height; - if (!isAttached()) { - return; - } - if (height != null && !"".equals(height)) { - DOM.setStyleAttribute(getElement(), "height", height); - int pixels = getElement().getOffsetHeight() - getExtraHeight(); - if (pixels < MIN_HEIGHT) { - pixels = MIN_HEIGHT; - int rootHeight = pixels + getExtraHeight(); - DOM.setStyleAttribute(getElement(), "height", (rootHeight) - + "px"); - - } - renderSpace.setHeight(pixels); - height = pixels + "px"; - contentPanel.getElement().getStyle().setProperty("height", height); - updateShadowSizeAndPosition(); - - } - } - - private int extraH = 0; - - private int getExtraHeight() { - extraH = header.getOffsetHeight() + footer.getOffsetHeight(); - return extraH; - } - - private void onDragEvent(Event event) { - switch (DOM.eventGetType(event)) { - case Event.ONMOUSEDOWN: - if (!isActive()) { - bringToFront(); - } - showDraggingCurtain(true); - dragging = true; - startX = DOM.eventGetScreenX(event); - startY = DOM.eventGetScreenY(event); - origX = DOM.getAbsoluteLeft(getElement()); - origY = DOM.getAbsoluteTop(getElement()); - DOM.setCapture(getElement()); - DOM.eventPreventDefault(event); - break; - case Event.ONMOUSEUP: - dragging = false; - showDraggingCurtain(false); - DOM.releaseCapture(getElement()); - break; - case Event.ONLOSECAPTURE: - showDraggingCurtain(false); - dragging = false; - break; - case Event.ONMOUSEMOVE: - if (dragging) { - centered = false; - final int x = DOM.eventGetScreenX(event) - startX + origX; - final int y = DOM.eventGetScreenY(event) - startY + origY; - setPopupPosition(x, y); - DOM.eventPreventDefault(event); - } - break; - default: - break; - } - } - - @Override - public boolean onEventPreview(Event event) { - if (dragging) { - onDragEvent(event); - return false; - } else if (resizing) { - onResizeEvent(event); - return false; - } else if (vaadinModality) { - // return false when modal and outside window - final Element target = event.getTarget().cast(); - if (!DOM.isOrHasChild(getElement(), target)) { - return false; - } - } - return true; - } - - public void onScroll(Widget widget, int scrollLeft, int scrollTop) { - client.updateVariable(id, "scrollTop", scrollTop, false); - client.updateVariable(id, "scrollLeft", scrollLeft, false); - } - - @Override - public void addStyleDependentName(String styleSuffix) { - // IWindow's getStyleElement() does not return the same element as - // getElement(), so we need to override this. - setStyleName(getElement(), getStylePrimaryName() + "-" + styleSuffix, - true); - } - - @Override - protected void onAttach() { - super.onAttach(); - - // Calculate space required by window borders, so we can accurately - // calculate space for content - - // IE (IE6 especially) requires some magic tricks to pull the border - // size correctly (remember that we want to accomodate for paddings as - // well) - if (BrowserInfo.get().isIE()) { - DOM.setStyleAttribute(contents, "width", "7000px"); - DOM.setStyleAttribute(contentPanel.getElement(), "width", "7000px"); - int contentWidth = DOM.getElementPropertyInt(contentPanel - .getElement(), "offsetWidth"); - contentWidth = DOM.getElementPropertyInt(contentPanel.getElement(), - "offsetWidth"); - final int windowWidth = DOM.getElementPropertyInt(getElement(), - "offsetWidth"); - DOM.setStyleAttribute(contentPanel.getElement(), "width", ""); - DOM.setStyleAttribute(contents, "width", ""); - - borderWidth = windowWidth - contentWidth; - } - - // Standards based browsers get away with it a little easier :) - else { - final int contentWidth = DOM.getElementPropertyInt(contentPanel - .getElement(), "offsetWidth"); - final int windowWidth = DOM.getElementPropertyInt(getElement(), - "offsetWidth"); - borderWidth = windowWidth - contentWidth; - } - - setWidth(width); - setHeight(height); - - } - - public RenderSpace getAllocatedSpace(Widget child) { - if (child == layout) { - return renderSpace; - } else { - // Exception ?? - return null; - } - } - - public boolean hasChildComponent(Widget component) { - if (component == layout) { - return true; - } else { - return false; - } - } - - public void replaceChildComponent(Widget oldComponent, Widget newComponent) { - contentPanel.setWidget(newComponent); - } - - public boolean requestLayout(Set child) { - if (dynamicWidth && !layoutRelativeWidth) { - setNaturalWidth(); - } - if (centered) { - center(); - } - updateShadowSizeAndPosition(); - return true; - } - - public void updateCaption(Paintable component, UIDL uidl) { - // NOP, window has own caption, layout captio not rendered - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/MenuBar.java b/src/com/vaadin/terminal/gwt/client/ui/MenuBar.java index a711864a37..88de95cb38 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/MenuBar.java +++ b/src/com/vaadin/terminal/gwt/client/ui/MenuBar.java @@ -376,7 +376,7 @@ public class MenuBar extends Widget implements PopupListener { // Create a new popup for this item, and position it next to // the item (below if this is a horizontal menu bar, to the // right if it's a vertical bar). - popup = new IToolkitOverlay(true) { + popup = new VToolkitOverlay(true) { { setWidget(item.getSubMenu()); item.getSubMenu().onShow(); diff --git a/src/com/vaadin/terminal/gwt/client/ui/TreeAction.java b/src/com/vaadin/terminal/gwt/client/ui/TreeAction.java index a77730f749..641b729521 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/TreeAction.java +++ b/src/com/vaadin/terminal/gwt/client/ui/TreeAction.java @@ -5,7 +5,7 @@ package com.vaadin.terminal.gwt.client.ui; /** - * This class is used for "row actions" in ITree and ITable + * This class is used for "row actions" in VTree and ITable */ public class TreeAction extends Action { diff --git a/src/com/vaadin/terminal/gwt/client/ui/VAbsoluteLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VAbsoluteLayout.java new file mode 100644 index 0000000000..b7b7aa106b --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VAbsoluteLayout.java @@ -0,0 +1,378 @@ +package com.vaadin.terminal.gwt.client.ui; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.Style; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.ComplexPanel; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.Container; +import com.vaadin.terminal.gwt.client.VCaption; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.RenderSpace; +import com.vaadin.terminal.gwt.client.UIDL; + +public class VAbsoluteLayout extends ComplexPanel implements Container { + + /** Tag name for widget creation */ + public static final String TAGNAME = "absolutelayout"; + + /** Class name, prefix in styling */ + public static final String CLASSNAME = "i-absolutelayout"; + + private DivElement marginElement; + + protected final Element canvas = DOM.createDiv(); + + private int excessPixelsHorizontal; + + private int excessPixelsVertical; + + private Object previousStyleName; + + private Map pidToComponentWrappper = new HashMap(); + + protected ApplicationConnection client; + + private boolean rendering; + + public VAbsoluteLayout() { + setElement(Document.get().createDivElement()); + setStyleName(CLASSNAME); + marginElement = Document.get().createDivElement(); + canvas.getStyle().setProperty("position", "relative"); + marginElement.appendChild(canvas); + getElement().appendChild(marginElement); + } + + public RenderSpace getAllocatedSpace(Widget child) { + // TODO needs some special handling for components with only on edge + // horizontally or vertically defined + AbsoluteWrapper wrapper = (AbsoluteWrapper) child.getParent(); + int w; + if (wrapper.left != null && wrapper.right != null) { + w = wrapper.getOffsetWidth(); + } else if (wrapper.right != null) { + // left == null + // available width == right edge == offsetleft + width + w = wrapper.getOffsetWidth() + wrapper.getElement().getOffsetLeft(); + } else { + // left != null && right == null || left == null && + // right == null + // available width == canvas width - offset left + w = canvas.getOffsetWidth() - wrapper.getElement().getOffsetLeft(); + } + int h; + if (wrapper.top != null && wrapper.bottom != null) { + h = wrapper.getOffsetHeight(); + } else if (wrapper.bottom != null) { + // top not defined, available space 0... bottom of wrapper + h = wrapper.getElement().getOffsetTop() + wrapper.getOffsetHeight(); + } else { + // top defined or both undefined, available space == canvas - top + h = canvas.getOffsetHeight() - wrapper.getElement().getOffsetTop(); + } + + return new RenderSpace(w, h); + } + + public boolean hasChildComponent(Widget component) { + for (Iterator> iterator = pidToComponentWrappper + .entrySet().iterator(); iterator.hasNext();) { + if (iterator.next().getValue().paintable == component) { + return true; + } + } + return false; + } + + public void replaceChildComponent(Widget oldComponent, Widget newComponent) { + for (Widget wrapper : getChildren()) { + AbsoluteWrapper w = (AbsoluteWrapper) wrapper; + if (w.getWidget() == oldComponent) { + w.setWidget(newComponent); + return; + } + } + } + + public boolean requestLayout(Set children) { + // component inside an absolute panel never affects parent nor the + // layout + return true; + } + + public void updateCaption(Paintable component, UIDL uidl) { + AbsoluteWrapper parent2 = (AbsoluteWrapper) ((Widget) component) + .getParent(); + parent2.updateCaption(uidl); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + rendering = true; + this.client = client; + // TODO margin handling + if (client.updateComponent(this, uidl, true)) { + rendering = false; + return; + } + + HashSet unrenderedPids = new HashSet( + pidToComponentWrappper.keySet()); + + for (Iterator childIterator = uidl.getChildIterator(); childIterator + .hasNext();) { + UIDL cc = childIterator.next(); + UIDL componentUIDL = cc.getChildUIDL(0); + unrenderedPids.remove(componentUIDL.getId()); + getWrapper(client, componentUIDL).updateFromUIDL(cc); + } + + for (String pid : unrenderedPids) { + AbsoluteWrapper absoluteWrapper = pidToComponentWrappper.get(pid); + pidToComponentWrappper.remove(pid); + absoluteWrapper.destroy(); + } + rendering = false; + } + + private AbsoluteWrapper getWrapper(ApplicationConnection client, + UIDL componentUIDL) { + AbsoluteWrapper wrapper = pidToComponentWrappper.get(componentUIDL + .getId()); + if (wrapper == null) { + wrapper = new AbsoluteWrapper(client.getPaintable(componentUIDL)); + pidToComponentWrappper.put(componentUIDL.getId(), wrapper); + add(wrapper); + } + return wrapper; + + } + + @Override + public void add(Widget child) { + super.add(child, canvas); + } + + @Override + public void setStyleName(String style) { + super.setStyleName(style); + if (previousStyleName == null || !previousStyleName.equals(style)) { + excessPixelsHorizontal = -1; + excessPixelsVertical = -1; + } + } + + @Override + public void setWidth(String width) { + super.setWidth(width); + // TODO do this so that canvas gets the sized properly (the area + // inside marginals) + canvas.getStyle().setProperty("width", width); + + if (!rendering) { + if (BrowserInfo.get().isIE6()) { + relayoutWrappersForIe6(); + } + relayoutRelativeChildren(); + } + } + + private void relayoutRelativeChildren() { + for (Widget widget : getChildren()) { + if (widget instanceof AbsoluteWrapper) { + AbsoluteWrapper w = (AbsoluteWrapper) widget; + client.handleComponentRelativeSize(w.getWidget()); + w.updateCaptionPosition(); + } + } + } + + @Override + public void setHeight(String height) { + super.setHeight(height); + // TODO do this so that canvas gets the sized properly (the area + // inside marginals) + canvas.getStyle().setProperty("height", height); + + if (!rendering) { + if (BrowserInfo.get().isIE6()) { + relayoutWrappersForIe6(); + } + relayoutRelativeChildren(); + } + } + + private void relayoutWrappersForIe6() { + for (Widget wrapper : getChildren()) { + if (wrapper instanceof AbsoluteWrapper) { + ((AbsoluteWrapper) wrapper).ie6Layout(); + } + } + } + + public class AbsoluteWrapper extends SimplePanel { + private String css; + private String left; + private String top; + private String right; + private String bottom; + private String zIndex; + + private Paintable paintable; + private VCaption caption; + + public AbsoluteWrapper(Paintable paintable) { + this.paintable = paintable; + setStyleName(CLASSNAME + "-wrapper"); + } + + public void updateCaption(UIDL uidl) { + + boolean captionIsNeeded = VCaption.isNeeded(uidl); + if (captionIsNeeded) { + if (caption == null) { + caption = new VCaption(paintable, client); + VAbsoluteLayout.this.add(caption); + } + caption.updateCaption(uidl); + updateCaptionPosition(); + } else { + if (caption != null) { + caption.removeFromParent(); + caption = null; + } + } + } + + public void destroy() { + if (caption != null) { + caption.removeFromParent(); + } + client.unregisterPaintable(paintable); + removeFromParent(); + } + + public void updateFromUIDL(UIDL componentUIDL) { + setPosition(componentUIDL.getStringAttribute("css")); + if (getWidget() != paintable) { + setWidget((Widget) paintable); + } + UIDL childUIDL = componentUIDL.getChildUIDL(0); + paintable.updateFromUIDL(childUIDL, client); + if (childUIDL.hasAttribute("cached")) { + // child may need relative size adjustment if wrapper details + // have changed this could be optimized (check if wrapper size + // has changed) + client.handleComponentRelativeSize((Widget) paintable); + } + } + + public void setPosition(String stringAttribute) { + if (css == null || !css.equals(stringAttribute)) { + css = stringAttribute; + top = right = bottom = left = zIndex = null; + if (!css.equals("")) { + String[] properties = css.split(";"); + for (int i = 0; i < properties.length; i++) { + String[] keyValue = properties[i].split(":"); + if (keyValue[0].equals("left")) { + left = keyValue[1]; + } else if (keyValue[0].equals("top")) { + top = keyValue[1]; + } else if (keyValue[0].equals("right")) { + right = keyValue[1]; + } else if (keyValue[0].equals("bottom")) { + bottom = keyValue[1]; + } else if (keyValue[0].equals("z-index")) { + zIndex = keyValue[1]; + } + } + } + // ensure ne values + Style style = getElement().getStyle(); + style.setProperty("zIndex", zIndex); + style.setProperty("top", top); + style.setProperty("left", left); + style.setProperty("right", right); + style.setProperty("bottom", bottom); + + if (BrowserInfo.get().isIE6()) { + ie6Layout(); + } + } + updateCaptionPosition(); + } + + private void updateCaptionPosition() { + if (caption != null) { + Style style = caption.getElement().getStyle(); + style.setProperty("position", "absolute"); + style.setPropertyPx("left", getElement().getOffsetLeft()); + style.setPropertyPx("top", getElement().getOffsetTop() + - caption.getHeight()); + } + } + + private void ie6Layout() { + // special handling for IE6 is needed, it does not support + // setting both left/right or top/bottom + Style style = getElement().getStyle(); + if (bottom != null && top != null) { + // define height for wrapper to simulate bottom property + int bottompixels = measureForIE6(bottom); + ApplicationConnection.getConsole().log("ALB" + bottompixels); + int height = canvas.getOffsetHeight() - bottompixels + - getElement().getOffsetTop(); + ApplicationConnection.getConsole().log("ALB" + height); + if (height < 0) { + height = 0; + } + style.setPropertyPx("height", height); + } else { + // reset possibly existing value + style.setProperty("height", ""); + } + if (left != null && right != null) { + // define width for wrapper to simulate right property + int rightPixels = measureForIE6(right); + ApplicationConnection.getConsole().log("ALR" + rightPixels); + int width = canvas.getOffsetWidth() - rightPixels + - getElement().getOffsetWidth(); + ApplicationConnection.getConsole().log("ALR" + width); + if (width < 0) { + width = 0; + } + style.setPropertyPx("width", width); + } else { + // reset possibly existing value + style.setProperty("width", ""); + } + } + + } + + private Element measureElement; + + private int measureForIE6(String cssLength) { + if (measureElement == null) { + measureElement = DOM.createDiv(); + measureElement.getStyle().setProperty("position", "absolute"); + canvas.appendChild(measureElement); + } + measureElement.getStyle().setProperty("width", cssLength); + return measureElement.getOffsetWidth(); + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VAccordion.java b/src/com/vaadin/terminal/gwt/client/ui/VAccordion.java new file mode 100644 index 0000000000..cda5b3a4dd --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VAccordion.java @@ -0,0 +1,647 @@ +package com.vaadin.terminal.gwt.client.ui; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.ClickListener; +import com.google.gwt.user.client.ui.ComplexPanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.ContainerResizedListener; +import com.vaadin.terminal.gwt.client.VCaption; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.RenderInformation; +import com.vaadin.terminal.gwt.client.RenderSpace; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +public class VAccordion extends VTabsheetBase implements + ContainerResizedListener { + + public static final String CLASSNAME = "i-accordion"; + + private Set paintables = new HashSet(); + + private String height; + + private String width = ""; + + private HashMap lazyUpdateMap = new HashMap(); + + private RenderSpace renderSpace = new RenderSpace(0, 0, true); + + private StackItem openTab = null; + + private boolean rendering = false; + + private int selectedUIDLItemIndex = -1; + + private RenderInformation renderInformation = new RenderInformation(); + + public VAccordion() { + super(CLASSNAME); + // IE6 needs this to calculate offsetHeight correctly + if (BrowserInfo.get().isIE6()) { + DOM.setStyleAttribute(getElement(), "zoom", "1"); + } + } + + @Override + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + rendering = true; + selectedUIDLItemIndex = -1; + super.updateFromUIDL(uidl, client); + /* + * Render content after all tabs have been created and we know how large + * the content area is + */ + if (selectedUIDLItemIndex >= 0) { + StackItem selectedItem = getStackItem(selectedUIDLItemIndex); + UIDL selectedTabUIDL = lazyUpdateMap.remove(selectedItem); + open(selectedUIDLItemIndex); + + selectedItem.setContent(selectedTabUIDL); + } else if (!uidl.getBooleanAttribute("cached") && openTab != null) { + close(openTab); + } + + iLayout(); + // finally render possible hidden tabs + if (lazyUpdateMap.size() > 0) { + for (Iterator iterator = lazyUpdateMap.keySet().iterator(); iterator + .hasNext();) { + StackItem item = (StackItem) iterator.next(); + item.setContent(lazyUpdateMap.get(item)); + } + lazyUpdateMap.clear(); + } + + renderInformation.updateSize(getElement()); + + rendering = false; + } + + @Override + protected void renderTab(UIDL tabUidl, int index, boolean selected, + boolean hidden) { + StackItem item; + int itemIndex; + if (getWidgetCount() <= index) { + // Create stackItem and render caption + item = new StackItem(tabUidl); + if (getWidgetCount() == 0) { + item.addStyleDependentName("first"); + } + itemIndex = getWidgetCount(); + add(item, getElement()); + } else { + item = getStackItem(index); + item = moveStackItemIfNeeded(item, index, tabUidl); + itemIndex = index; + } + item.updateCaption(tabUidl); + + item.setVisible(!hidden); + + if (selected) { + selectedUIDLItemIndex = itemIndex; + } + + if (tabUidl.getChildCount() > 0) { + lazyUpdateMap.put(item, tabUidl.getChildUIDL(0)); + } + } + + /** + * This method tries to find out if a tab has been rendered with a different + * index previously. If this is the case it re-orders the children so the + * same StackItem is used for rendering this time. E.g. if the first tab has + * been removed all tabs which contain cached content must be moved 1 step + * up to preserve the cached content. + * + * @param item + * @param newIndex + * @param tabUidl + * @return + */ + private StackItem moveStackItemIfNeeded(StackItem item, int newIndex, + UIDL tabUidl) { + UIDL tabContentUIDL = null; + Paintable tabContent = null; + if (tabUidl.getChildCount() > 0) { + tabContentUIDL = tabUidl.getChildUIDL(0); + tabContent = client.getPaintable(tabContentUIDL); + } + + Widget itemWidget = item.getComponent(); + if (tabContent != null) { + if (tabContent != itemWidget) { + /* + * This is not the same widget as before, find out if it has + * been moved + */ + int oldIndex = -1; + StackItem oldItem = null; + for (int i = 0; i < getWidgetCount(); i++) { + Widget w = getWidget(i); + oldItem = (StackItem) w; + if (tabContent == oldItem.getComponent()) { + oldIndex = i; + break; + } + } + + if (oldIndex != -1 && oldIndex > newIndex) { + /* + * The tab has previously been rendered in another position + * so we must move the cached content to correct position. + * We move only items with oldIndex > newIndex to prevent + * moving items already rendered in this update. If for + * instance tabs 1,2,3 are removed and added as 3,2,1 we + * cannot re-use "1" when we get to the third tab. + */ + insert(oldItem, getElement(), newIndex, true); + return oldItem; + } + } + } else { + // Tab which has never been loaded. Must assure we use an empty + // StackItem + Widget oldWidget = item.getComponent(); + if (oldWidget != null) { + item = new StackItem(tabUidl); + insert(item, getElement(), newIndex, true); + } + } + return item; + } + + private void open(int itemIndex) { + StackItem item = (StackItem) getWidget(itemIndex); + boolean alreadyOpen = false; + if (openTab != null) { + if (openTab.isOpen()) { + if (openTab == item) { + alreadyOpen = true; + } else { + openTab.close(); + } + } + } + + if (!alreadyOpen) { + item.open(); + activeTabIndex = itemIndex; + openTab = item; + } + + // Update the size for the open tab + updateOpenTabSize(); + } + + private void close(StackItem item) { + if (!item.isOpen()) { + return; + } + + item.close(); + activeTabIndex = -1; + openTab = null; + + } + + @Override + protected void selectTab(final int index, final UIDL contentUidl) { + StackItem item = getStackItem(index); + if (index != activeTabIndex) { + open(index); + iLayout(); + // TODO Check if this is needed + client.runDescendentsLayout(this); + + } + item.setContent(contentUidl); + } + + public void onSelectTab(StackItem item) { + final int index = getWidgetIndex(item); + if (index != activeTabIndex && !disabled && !readonly + && !disabledTabKeys.contains(tabKeys.get(index))) { + addStyleDependentName("loading"); + client + .updateVariable(id, "selected", "" + tabKeys.get(index), + true); + } + } + + @Override + public void setWidth(String width) { + if (this.width.equals(width)) { + return; + } + + super.setWidth(width); + this.width = width; + if (!rendering) { + updateOpenTabSize(); + + if (isDynamicHeight()) { + Util.updateRelativeChildrenAndSendSizeUpdateEvent(client, + openTab, this); + updateOpenTabSize(); + } + + if (isDynamicHeight()) { + openTab.setHeightFromWidget(); + } + iLayout(); + } + } + + @Override + public void setHeight(String height) { + super.setHeight(height); + this.height = height; + + if (!rendering) { + updateOpenTabSize(); + } + + } + + /** + * Sets the size of the open tab + */ + private void updateOpenTabSize() { + if (openTab == null) { + renderSpace.setHeight(0); + renderSpace.setWidth(0); + return; + } + + // WIDTH + if (!isDynamicWidth()) { + int w = getOffsetWidth(); + openTab.setWidth(w); + renderSpace.setWidth(w); + } else { + renderSpace.setWidth(0); + } + + // HEIGHT + if (!isDynamicHeight()) { + int usedPixels = 0; + for (Widget w : getChildren()) { + StackItem item = (StackItem) w; + if (item == openTab) { + usedPixels += item.getCaptionHeight(); + } else { + // This includes the captionNode borders + usedPixels += item.getHeight(); + } + } + + int offsetHeight = getOffsetHeight(); + + int spaceForOpenItem = offsetHeight - usedPixels; + + if (spaceForOpenItem < 0) { + spaceForOpenItem = 0; + } + + renderSpace.setHeight(spaceForOpenItem); + openTab.setHeight(spaceForOpenItem); + } else { + renderSpace.setHeight(0); + openTab.setHeightFromWidget(); + + } + + } + + public void iLayout() { + if (openTab == null) { + return; + } + + if (isDynamicWidth()) { + int maxWidth = 40; + for (Widget w : getChildren()) { + StackItem si = (StackItem) w; + int captionWidth = si.getCaptionWidth(); + if (captionWidth > maxWidth) { + maxWidth = captionWidth; + } + } + int widgetWidth = openTab.getWidgetWidth(); + if (widgetWidth > maxWidth) { + maxWidth = widgetWidth; + } + super.setWidth(maxWidth + "px"); + openTab.setWidth(maxWidth); + } + + Util.runWebkitOverflowAutoFix(openTab.getContainerElement()); + + } + + /** + * + */ + protected class StackItem extends ComplexPanel implements ClickListener { + + public void setHeight(int height) { + if (height == -1) { + super.setHeight(""); + DOM.setStyleAttribute(content, "height", "0px"); + } else { + super.setHeight((height + getCaptionHeight()) + "px"); + DOM.setStyleAttribute(content, "height", height + "px"); + DOM + .setStyleAttribute(content, "top", getCaptionHeight() + + "px"); + + } + } + + public Widget getComponent() { + if (getWidgetCount() < 2) { + return null; + } + return getWidget(1); + } + + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + } + + public void setHeightFromWidget() { + Widget paintable = getPaintable(); + if (paintable == null) { + return; + } + + int paintableHeight = (paintable).getElement().getOffsetHeight(); + setHeight(paintableHeight); + + } + + /** + * Returns caption width including padding + * + * @return + */ + public int getCaptionWidth() { + if (caption == null) { + return 0; + } + + int captionWidth = caption.getRequiredWidth(); + int padding = Util.measureHorizontalPaddingAndBorder(caption + .getElement(), 18); + return captionWidth + padding; + } + + public void setWidth(int width) { + if (width == -1) { + super.setWidth(""); + } else { + super.setWidth(width + "px"); + } + } + + public int getHeight() { + return getOffsetHeight(); + } + + public int getCaptionHeight() { + return captionNode.getOffsetHeight(); + } + + private VCaption caption; + private boolean open = false; + private Element content = DOM.createDiv(); + private Element captionNode = DOM.createDiv(); + + public StackItem(UIDL tabUidl) { + setElement(DOM.createDiv()); + caption = new VCaption(null, client); + caption.addClickListener(this); + if (BrowserInfo.get().isIE6()) { + DOM.setEventListener(captionNode, this); + DOM.sinkEvents(captionNode, Event.BUTTON_LEFT); + } + super.add(caption, captionNode); + DOM.appendChild(captionNode, caption.getElement()); + DOM.appendChild(getElement(), captionNode); + DOM.appendChild(getElement(), content); + setStylePrimaryName(CLASSNAME + "-item"); + DOM.setElementProperty(content, "className", CLASSNAME + + "-item-content"); + DOM.setElementProperty(captionNode, "className", CLASSNAME + + "-item-caption"); + close(); + } + + @Override + public void onBrowserEvent(Event event) { + onSelectTab(this); + } + + public Element getContainerElement() { + return content; + } + + public Widget getPaintable() { + if (getWidgetCount() > 1) { + return getWidget(1); + } else { + return null; + } + } + + public void replacePaintable(Paintable newPntbl) { + if (getWidgetCount() > 1) { + client.unregisterPaintable((Paintable) getWidget(1)); + paintables.remove(getWidget(1)); + remove(1); + } + add((Widget) newPntbl, content); + paintables.add(newPntbl); + } + + public void open() { + open = true; + DOM.setStyleAttribute(content, "top", getCaptionHeight() + "px"); + DOM.setStyleAttribute(content, "left", "0px"); + DOM.setStyleAttribute(content, "visibility", ""); + addStyleDependentName("open"); + } + + public void hide() { + DOM.setStyleAttribute(content, "visibility", "hidden"); + } + + public void close() { + DOM.setStyleAttribute(content, "visibility", "hidden"); + DOM.setStyleAttribute(content, "top", "-100000px"); + DOM.setStyleAttribute(content, "left", "-100000px"); + removeStyleDependentName("open"); + setHeight(-1); + open = false; + } + + public boolean isOpen() { + return open; + } + + public void setContent(UIDL contentUidl) { + final Paintable newPntbl = client.getPaintable(contentUidl); + if (getPaintable() == null) { + add((Widget) newPntbl, content); + paintables.add(newPntbl); + } else if (getPaintable() != newPntbl) { + replacePaintable(newPntbl); + } + newPntbl.updateFromUIDL(contentUidl, client); + if (contentUidl.getBooleanAttribute("cached")) { + /* + * The size of a cached, relative sized component must be + * updated to report correct size. + */ + client.handleComponentRelativeSize((Widget) newPntbl); + } + if (isOpen() && isDynamicHeight()) { + setHeightFromWidget(); + } + } + + public void onClick(Widget sender) { + onSelectTab(this); + } + + public void updateCaption(UIDL uidl) { + caption.updateCaption(uidl); + } + + public int getWidgetWidth() { + return DOM.getFirstChild(content).getOffsetWidth(); + } + + public boolean contains(Paintable p) { + return (getPaintable() == p); + } + + public boolean isCaptionVisible() { + return caption.isVisible(); + } + + } + + @Override + protected void clearPaintables() { + clear(); + } + + public boolean isDynamicHeight() { + return height == null || height.equals(""); + } + + public boolean isDynamicWidth() { + return width == null || width.equals(""); + } + + @Override + protected Iterator getPaintableIterator() { + return paintables.iterator(); + } + + public boolean hasChildComponent(Widget component) { + if (paintables.contains(component)) { + return true; + } else { + return false; + } + } + + public void replaceChildComponent(Widget oldComponent, Widget newComponent) { + for (Widget w : getChildren()) { + StackItem item = (StackItem) w; + if (item.getPaintable() == oldComponent) { + item.replacePaintable((Paintable) newComponent); + return; + } + } + } + + public void updateCaption(Paintable component, UIDL uidl) { + /* Accordion does not render its children's captions */ + } + + public boolean requestLayout(Set child) { + if (!isDynamicHeight() && !isDynamicWidth()) { + /* + * If the height and width has been specified for this container the + * child components cannot make the size of the layout change + */ + + return true; + } + + updateOpenTabSize(); + + if (renderInformation.updateSize(getElement())) { + /* + * Size has changed so we let the child components know about the + * new size. + */ + iLayout(); + // TODO Check if this is needed + client.runDescendentsLayout(this); + + return false; + } else { + /* + * Size has not changed so we do not need to propagate the event + * further + */ + return true; + } + + } + + public RenderSpace getAllocatedSpace(Widget child) { + return renderSpace; + } + + @Override + protected int getTabCount() { + return getWidgetCount(); + } + + @Override + protected void removeTab(int index) { + StackItem item = getStackItem(index); + remove(item); + } + + @Override + protected Paintable getTab(int index) { + if (index < getWidgetCount()) { + return (Paintable) (getStackItem(index)).getPaintable(); + } + + return null; + } + + private StackItem getStackItem(int index) { + return (StackItem) getWidget(index); + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VButton.java b/src/com/vaadin/terminal/gwt/client/ui/VButton.java new file mode 100644 index 0000000000..789a0f35d1 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VButton.java @@ -0,0 +1,190 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.ClickListener; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.VTooltip; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +public class VButton extends Button implements Paintable { + + private String width = null; + + public static final String CLASSNAME = "i-button"; + + // Used only for IE, because it doesn't support :active CSS selector + private static final String CLASSNAME_DOWN = "i-pressed"; + + String id; + + ApplicationConnection client; + + private Element errorIndicatorElement; + + private final Element captionElement = DOM.createSpan(); + + private Icon icon; + + /** + * Helper flat to handle special-case where the button is moved from under + * mouse while clicking it. In this case mouse leaves the button without + * moving. + */ + private boolean clickPending; + + public VButton() { + setStyleName(CLASSNAME); + + DOM.appendChild(getElement(), captionElement); + + addClickListener(new ClickListener() { + public void onClick(Widget sender) { + if (id == null || client == null) { + return; + } + /* + * TODO isolate workaround. Safari don't always seem to fire + * onblur previously focused component before button is clicked. + */ + VButton.this.setFocus(true); + client.updateVariable(id, "state", true, true); + clickPending = false; + } + }); + sinkEvents(VTooltip.TOOLTIP_EVENTS); + sinkEvents(Event.ONMOUSEDOWN); + sinkEvents(Event.ONMOUSEUP); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + + // Ensure correct implementation, + // but don't let container manage caption etc. + if (client.updateComponent(this, uidl, false)) { + return; + } + + // Save details + this.client = client; + id = uidl.getId(); + + // Set text + setText(uidl.getStringAttribute("caption")); + + // handle error + if (uidl.hasAttribute("error")) { + if (errorIndicatorElement == null) { + errorIndicatorElement = DOM.createDiv(); + DOM.setElementProperty(errorIndicatorElement, "className", + "i-errorindicator"); + } + DOM.insertChild(getElement(), errorIndicatorElement, 0); + + // Fix for IE6, IE7 + if (BrowserInfo.get().isIE()) { + DOM.setInnerText(errorIndicatorElement, " "); + } + + } else if (errorIndicatorElement != null) { + DOM.removeChild(getElement(), errorIndicatorElement); + errorIndicatorElement = null; + } + + if (uidl.hasAttribute("readonly")) { + setEnabled(false); + } + + if (uidl.hasAttribute("icon")) { + if (icon == null) { + icon = new Icon(client); + DOM.insertChild(getElement(), icon.getElement(), 0); + } + icon.setUri(uidl.getStringAttribute("icon")); + } else { + if (icon != null) { + DOM.removeChild(getElement(), icon.getElement()); + icon = null; + } + } + if (BrowserInfo.get().isIE7()) { + if (width.equals("")) { + setWidth(getOffsetWidth() + "px"); + } + } + } + + @Override + public void setText(String text) { + DOM.setInnerText(captionElement, text); + } + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + + if (DOM.eventGetType(event) == Event.ONLOAD) { + Util.notifyParentOfSizeChange(this, true); + + } else if (DOM.eventGetType(event) == Event.ONMOUSEDOWN + && event.getButton() == Event.BUTTON_LEFT) { + clickPending = true; + if (BrowserInfo.get().isIE()) { + // Only for IE, because it doesn't support :active CSS selector + // Simple check is cheaper than DOM manipulation + addStyleName(CLASSNAME_DOWN); + } + } else if (DOM.eventGetType(event) == Event.ONMOUSEMOVE) { + clickPending = false; + } else if (DOM.eventGetType(event) == Event.ONMOUSEOUT) { + if (clickPending) { + click(); + } + if (BrowserInfo.get().isIE()) { + removeStyleName(CLASSNAME_DOWN); + } + clickPending = false; + } else if (DOM.eventGetType(event) == Event.ONMOUSEUP) { + if (BrowserInfo.get().isIE()) { + removeStyleName(CLASSNAME_DOWN); + } + } + + if (client != null) { + client.handleTooltipEvent(event, this); + } + } + + @Override + public void setWidth(String width) { + /* Workaround for IE7 button size part 1 (#2014) */ + if (BrowserInfo.get().isIE7() && this.width != null) { + if (this.width.equals(width)) { + return; + } + + if (width == null) { + width = ""; + } + } + + this.width = width; + super.setWidth(width); + + /* Workaround for IE7 button size part 2 (#2014) */ + if (BrowserInfo.get().isIE7()) { + super.setWidth(width); + } + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VCalendarPanel.java b/src/com/vaadin/terminal/gwt/client/ui/VCalendarPanel.java new file mode 100644 index 0000000000..afb3349ea7 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VCalendarPanel.java @@ -0,0 +1,520 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.ui.FlexTable; +import com.google.gwt.user.client.ui.MouseListener; +import com.google.gwt.user.client.ui.MouseListenerCollection; +import com.google.gwt.user.client.ui.SourcesMouseEvents; +import com.google.gwt.user.client.ui.SourcesTableEvents; +import com.google.gwt.user.client.ui.TableListener; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.DateTimeService; +import com.vaadin.terminal.gwt.client.LocaleService; + +public class VCalendarPanel extends FlexTable implements MouseListener { + + private final VDateField datefield; + + private VEventButton prevYear; + + private VEventButton nextYear; + + private VEventButton prevMonth; + + private VEventButton nextMonth; + + private VTime time; + + private Date minDate = null; + + private Date maxDate = null; + + private CalendarEntrySource entrySource; + + /* Needed to identify resolution changes */ + private int resolution = VDateField.RESOLUTION_YEAR; + + /* Needed to identify locale changes */ + private String locale = LocaleService.getDefaultLocale(); + + public VCalendarPanel(VDateField parent) { + datefield = parent; + setStyleName(VDateField.CLASSNAME + "-calendarpanel"); + // buildCalendar(true); + addTableListener(new DateClickListener(this)); + } + + public VCalendarPanel(VDateField parent, Date min, Date max) { + datefield = parent; + setStyleName(VDateField.CLASSNAME + "-calendarpanel"); + // buildCalendar(true); + addTableListener(new DateClickListener(this)); + + } + + private void buildCalendar(boolean forceRedraw) { + final boolean needsMonth = datefield.getCurrentResolution() > VDateField.RESOLUTION_YEAR; + boolean needsBody = datefield.getCurrentResolution() >= VDateField.RESOLUTION_DAY; + final boolean needsTime = datefield.getCurrentResolution() >= VDateField.RESOLUTION_HOUR; + forceRedraw = prevYear == null ? true : forceRedraw; + buildCalendarHeader(forceRedraw, needsMonth); + clearCalendarBody(!needsBody); + if (needsBody) { + buildCalendarBody(); + } + if (needsTime) { + buildTime(forceRedraw); + } else if (time != null) { + remove(time); + time = null; + } + } + + private void clearCalendarBody(boolean remove) { + if (!remove) { + for (int row = 2; row < 8; row++) { + for (int col = 0; col < 7; col++) { + setHTML(row, col, " "); + } + } + } else if (getRowCount() > 2) { + while (getRowCount() > 2) { + removeRow(2); + } + } + } + + private void buildCalendarHeader(boolean forceRedraw, boolean needsMonth) { + if (forceRedraw) { + if (prevMonth == null) { // Only do once + prevYear = new VEventButton(); + prevYear.setHTML("«"); + prevYear.setStyleName("i-button-prevyear"); + nextYear = new VEventButton(); + nextYear.setHTML("»"); + nextYear.setStyleName("i-button-nextyear"); + prevYear.addMouseListener(this); + nextYear.addMouseListener(this); + setWidget(0, 0, prevYear); + setWidget(0, 4, nextYear); + + if (needsMonth) { + prevMonth = new VEventButton(); + prevMonth.setHTML("‹"); + prevMonth.setStyleName("i-button-prevmonth"); + nextMonth = new VEventButton(); + nextMonth.setHTML("›"); + nextMonth.setStyleName("i-button-nextmonth"); + prevMonth.addMouseListener(this); + nextMonth.addMouseListener(this); + setWidget(0, 3, nextMonth); + setWidget(0, 1, prevMonth); + } + + getFlexCellFormatter().setColSpan(0, 2, 3); + getRowFormatter().addStyleName(0, + VDateField.CLASSNAME + "-calendarpanel-header"); + } else if (!needsMonth) { + // Remove month traverse buttons + prevMonth.removeMouseListener(this); + nextMonth.removeMouseListener(this); + remove(prevMonth); + remove(nextMonth); + prevMonth = null; + nextMonth = null; + } + + // Print weekday names + final int firstDay = datefield.getDateTimeService() + .getFirstDayOfWeek(); + for (int i = 0; i < 7; i++) { + int day = i + firstDay; + if (day > 6) { + day = 0; + } + if (datefield.getCurrentResolution() > VDateField.RESOLUTION_MONTH) { + setHTML(1, i, "" + + datefield.getDateTimeService().getShortDay(day) + + ""); + } else { + setHTML(1, i, ""); + } + } + } + + final String monthName = needsMonth ? datefield.getDateTimeService() + .getMonth(datefield.getShowingDate().getMonth()) : ""; + final int year = datefield.getShowingDate().getYear() + 1900; + setHTML(0, 2, "" + monthName + " " + year + + ""); + } + + private void buildCalendarBody() { + // date actually selected? + Date currentDate = datefield.getCurrentDate(); + Date showing = datefield.getShowingDate(); + boolean selected = (currentDate != null + && currentDate.getMonth() == showing.getMonth() && currentDate + .getYear() == showing.getYear()); + + final int startWeekDay = datefield.getDateTimeService() + .getStartWeekDay(datefield.getShowingDate()); + final int numDays = DateTimeService.getNumberOfDaysInMonth(datefield + .getShowingDate()); + int dayCount = 0; + final Date today = new Date(); + final Date curr = new Date(datefield.getShowingDate().getTime()); + for (int row = 2; row < 8; row++) { + for (int col = 0; col < 7; col++) { + if (!(row == 2 && col < startWeekDay)) { + if (dayCount < numDays) { + final int selectedDate = ++dayCount; + String title = ""; + if (entrySource != null) { + curr.setDate(dayCount); + final List entries = entrySource.getEntries(curr, + VDateField.RESOLUTION_DAY); + if (entries != null) { + for (final Iterator it = entries.iterator(); it + .hasNext();) { + final CalendarEntry entry = (CalendarEntry) it + .next(); + title += (title.length() > 0 ? ", " : "") + + entry.getStringForDate(curr); + } + } + } + final String baseclass = VDateField.CLASSNAME + + "-calendarpanel-day"; + String cssClass = baseclass; + if (!isEnabledDate(curr)) { + cssClass += " " + baseclass + "-disabled"; + } + if (selected + && datefield.getShowingDate().getDate() == dayCount) { + cssClass += " " + baseclass + "-selected"; + } + if (today.getDate() == dayCount + && today.getMonth() == datefield + .getShowingDate().getMonth() + && today.getYear() == datefield + .getShowingDate().getYear()) { + cssClass += " " + baseclass + "-today"; + } + if (title.length() > 0) { + cssClass += " " + baseclass + "-entry"; + } + setHTML(row, col, "" + + selectedDate + ""); + } else { + break; + } + + } + } + } + } + + private void buildTime(boolean forceRedraw) { + if (time == null) { + time = new VTime(datefield); + setText(8, 0, ""); // Add new row + getFlexCellFormatter().setColSpan(8, 0, 7); + setWidget(8, 0, time); + } + time.updateTime(forceRedraw); + } + + /** + * + * @param forceRedraw + * Build all from scratch, in case of e.g. locale changes + */ + public void updateCalendar() { + // Locale and resolution changes force a complete redraw + buildCalendar(locale != datefield.getCurrentLocale() + || resolution != datefield.getCurrentResolution()); + if (datefield instanceof VTextualDate) { + ((VTextualDate) datefield).buildDate(); + } + locale = datefield.getCurrentLocale(); + resolution = datefield.getCurrentResolution(); + } + + private boolean isEnabledDate(Date date) { + if ((minDate != null && date.before(minDate)) + || (maxDate != null && date.after(maxDate))) { + return false; + } + return true; + } + + private void processClickEvent(Widget sender, boolean updateVariable) { + if (!datefield.isEnabled() || datefield.isReadonly()) { + return; + } + Date showingDate = datefield.getShowingDate(); + if (!updateVariable) { + if (sender == prevYear) { + showingDate.setYear(showingDate.getYear() - 1); + updateCalendar(); + } else if (sender == nextYear) { + showingDate.setYear(showingDate.getYear() + 1); + updateCalendar(); + } else if (sender == prevMonth) { + int currentMonth = showingDate.getMonth(); + showingDate.setMonth(currentMonth - 1); + + /* + * If the selected date was e.g. 31.12 the new date would be + * 31.11 but this date is invalid so the new date will be 1.12. + * This is taken care of by decreasing the date until we have + * the correct month. + */ + while (showingDate.getMonth() == currentMonth) { + showingDate.setDate(showingDate.getDate() - 1); + } + + updateCalendar(); + } else if (sender == nextMonth) { + int currentMonth = showingDate.getMonth(); + showingDate.setMonth(currentMonth + 1); + int requestedMonth = (currentMonth + 1) % 12; + + /* + * If the selected date was e.g. 31.3 the new date would be 31.4 + * but this date is invalid so the new date will be 1.5. This is + * taken care of by decreasing the date until we have the + * correct month. + */ + while (showingDate.getMonth() != requestedMonth) { + showingDate.setDate(showingDate.getDate() - 1); + } + + updateCalendar(); + } + } else { + if (datefield.getCurrentResolution() == VDateField.RESOLUTION_YEAR + || datefield.getCurrentResolution() == VDateField.RESOLUTION_MONTH) { + // Due to current UI, update variable if res=year/month + datefield.setCurrentDate(new Date(showingDate.getTime())); + if (datefield.getCurrentResolution() == VDateField.RESOLUTION_MONTH) { + datefield.getClient().updateVariable(datefield.getId(), + "month", datefield.getCurrentDate().getMonth() + 1, + false); + } + datefield.getClient().updateVariable(datefield.getId(), "year", + datefield.getCurrentDate().getYear() + 1900, + datefield.isImmediate()); + + /* Must update the value in the textfield also */ + updateCalendar(); + } + } + } + + private Timer timer; + + public void onMouseDown(final Widget sender, int x, int y) { + // Allow user to click-n-hold for fast-forward or fast-rewind. + // Timer is first used for a 500ms delay after mousedown. After that has + // elapsed, another timer is triggered to go off every 150ms. Both + // timers are cancelled on mouseup or mouseout. + if (sender instanceof VEventButton) { + processClickEvent(sender, false); + timer = new Timer() { + @Override + public void run() { + timer = new Timer() { + @Override + public void run() { + processClickEvent(sender, false); + } + }; + timer.scheduleRepeating(150); + } + }; + timer.schedule(500); + } + } + + public void onMouseEnter(Widget sender) { + } + + public void onMouseLeave(Widget sender) { + if (timer != null) { + timer.cancel(); + } + } + + public void onMouseMove(Widget sender, int x, int y) { + } + + public void onMouseUp(Widget sender, int x, int y) { + if (timer != null) { + timer.cancel(); + } + processClickEvent(sender, true); + } + + private class VEventButton extends VButton implements SourcesMouseEvents { + + private MouseListenerCollection mouseListeners; + + public VEventButton() { + super(); + sinkEvents(Event.FOCUSEVENTS | Event.KEYEVENTS | Event.ONCLICK + | Event.MOUSEEVENTS); + } + + public void addMouseListener(MouseListener listener) { + if (mouseListeners == null) { + mouseListeners = new MouseListenerCollection(); + } + mouseListeners.add(listener); + } + + public void removeMouseListener(MouseListener listener) { + if (mouseListeners != null) { + mouseListeners.remove(listener); + } + } + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + switch (DOM.eventGetType(event)) { + case Event.ONMOUSEDOWN: + case Event.ONMOUSEUP: + case Event.ONMOUSEMOVE: + case Event.ONMOUSEOVER: + case Event.ONMOUSEOUT: + if (mouseListeners != null) { + mouseListeners.fireMouseEvent(this, event); + } + break; + } + } + } + + private class DateClickListener implements TableListener { + + private final VCalendarPanel cal; + + public DateClickListener(VCalendarPanel panel) { + cal = panel; + } + + public void onCellClicked(SourcesTableEvents sender, int row, int col) { + if (sender != cal || row < 2 || row > 7 + || !cal.datefield.isEnabled() || cal.datefield.isReadonly()) { + return; + } + + final String text = cal.getText(row, col); + if (text.equals(" ")) { + return; + } + + try { + final Integer day = new Integer(text); + final Date newDate = cal.datefield.getShowingDate(); + newDate.setDate(day.intValue()); + if (!isEnabledDate(newDate)) { + return; + } + if (cal.datefield.getCurrentDate() == null) { + cal.datefield.setCurrentDate(new Date(newDate.getTime())); + + // Init variables with current time + datefield.getClient().updateVariable(cal.datefield.getId(), + "hour", newDate.getHours(), false); + datefield.getClient().updateVariable(cal.datefield.getId(), + "min", newDate.getMinutes(), false); + datefield.getClient().updateVariable(cal.datefield.getId(), + "sec", newDate.getSeconds(), false); + datefield.getClient().updateVariable(cal.datefield.getId(), + "msec", datefield.getMilliseconds(), false); + } + + cal.datefield.getCurrentDate().setTime(newDate.getTime()); + cal.datefield.getClient().updateVariable(cal.datefield.getId(), + "day", cal.datefield.getCurrentDate().getDate(), false); + cal.datefield.getClient().updateVariable(cal.datefield.getId(), + "month", cal.datefield.getCurrentDate().getMonth() + 1, + false); + cal.datefield.getClient().updateVariable(cal.datefield.getId(), + "year", + cal.datefield.getCurrentDate().getYear() + 1900, + cal.datefield.isImmediate()); + + if (datefield instanceof VTextualDate + && resolution < VDateField.RESOLUTION_HOUR) { + ((VToolkitOverlay) getParent()).hide(); + } else { + updateCalendar(); + } + + } catch (final NumberFormatException e) { + // Not a number, ignore and stop here + return; + } + } + + } + + public void setLimits(Date min, Date max) { + if (min != null) { + final Date d = new Date(min.getTime()); + d.setHours(0); + d.setMinutes(0); + d.setSeconds(1); + minDate = d; + } else { + minDate = null; + } + if (max != null) { + final Date d = new Date(max.getTime()); + d.setHours(24); + d.setMinutes(59); + d.setSeconds(59); + maxDate = d; + } else { + maxDate = null; + } + } + + public void setCalendarEntrySource(CalendarEntrySource entrySource) { + this.entrySource = entrySource; + } + + public CalendarEntrySource getCalendarEntrySource() { + return entrySource; + } + + public interface CalendarEntrySource { + public List getEntries(Date date, int resolution); + } + + /** + * Sets focus to Calendar panel. + * + * @param focus + */ + public void setFocus(boolean focus) { + nextYear.setFocus(focus); + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VCheckBox.java b/src/com/vaadin/terminal/gwt/client/ui/VCheckBox.java new file mode 100644 index 0000000000..2c6470f0d8 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VCheckBox.java @@ -0,0 +1,140 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.ClickListener; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.VTooltip; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +public class VCheckBox extends com.google.gwt.user.client.ui.CheckBox implements + Paintable, Field { + + public static final String CLASSNAME = "i-checkbox"; + + String id; + + boolean immediate; + + ApplicationConnection client; + + private Element errorIndicatorElement; + + private Icon icon; + + private boolean isBlockMode = false; + + public VCheckBox() { + setStyleName(CLASSNAME); + addClickListener(new ClickListener() { + + public void onClick(Widget sender) { + if (id == null || client == null) { + return; + } + client.updateVariable(id, "state", isChecked(), immediate); + } + + }); + sinkEvents(VTooltip.TOOLTIP_EVENTS); + Element el = DOM.getFirstChild(getElement()); + while (el != null) { + DOM.sinkEvents(el, + (DOM.getEventsSunk(el) | VTooltip.TOOLTIP_EVENTS)); + el = DOM.getNextSibling(el); + } + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + // Save details + this.client = client; + id = uidl.getId(); + + // Ensure correct implementation + if (client.updateComponent(this, uidl, false)) { + return; + } + + if (uidl.hasAttribute("error")) { + if (errorIndicatorElement == null) { + errorIndicatorElement = DOM.createDiv(); + errorIndicatorElement.setInnerHTML(" "); + DOM.setElementProperty(errorIndicatorElement, "className", + "i-errorindicator"); + DOM.appendChild(getElement(), errorIndicatorElement); + DOM.sinkEvents(errorIndicatorElement, VTooltip.TOOLTIP_EVENTS + | Event.ONCLICK); + } + } else if (errorIndicatorElement != null) { + DOM.setStyleAttribute(errorIndicatorElement, "display", "none"); + } + + if (uidl.hasAttribute("readonly")) { + setEnabled(false); + } + + if (uidl.hasAttribute("icon")) { + if (icon == null) { + icon = new Icon(client); + DOM.insertChild(getElement(), icon.getElement(), 1); + icon.sinkEvents(VTooltip.TOOLTIP_EVENTS); + icon.sinkEvents(Event.ONCLICK); + } + icon.setUri(uidl.getStringAttribute("icon")); + } else if (icon != null) { + // detach icon + DOM.removeChild(getElement(), icon.getElement()); + icon = null; + } + + // Set text + setText(uidl.getStringAttribute("caption")); + setChecked(uidl.getBooleanVariable("state")); + immediate = uidl.getBooleanAttribute("immediate"); + } + + @Override + public void onBrowserEvent(Event event) { + if (icon != null && (event.getTypeInt() == Event.ONCLICK) + && (event.getTarget() == icon.getElement())) { + setChecked(!isChecked()); + } + super.onBrowserEvent(event); + if (event.getTypeInt() == Event.ONLOAD) { + Util.notifyParentOfSizeChange(this, true); + } + if (client != null) { + client.handleTooltipEvent(event, this); + } + } + + @Override + public void setWidth(String width) { + setBlockMode(); + super.setWidth(width); + } + + @Override + public void setHeight(String height) { + setBlockMode(); + super.setHeight(height); + } + + /** + * makes container element (span) to be block element to enable sizing. + */ + private void setBlockMode() { + if (!isBlockMode) { + DOM.setStyleAttribute(getElement(), "display", "block"); + isBlockMode = true; + } + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VContextMenu.java b/src/com/vaadin/terminal/gwt/client/ui/VContextMenu.java new file mode 100644 index 0000000000..f0d1e87a8f --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VContextMenu.java @@ -0,0 +1,157 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.dom.client.NodeList; +import com.google.gwt.dom.client.TableRowElement; +import com.google.gwt.dom.client.TableSectionElement; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.ui.MenuBar; +import com.google.gwt.user.client.ui.MenuItem; +import com.google.gwt.user.client.ui.PopupPanel; + +public class VContextMenu extends VToolkitOverlay implements SubPartAware { + + private ActionOwner actionOwner; + + private final CMenuBar menu = new CMenuBar(); + + private int left; + + private int top; + + /** + * This method should be used only by Client object as only one per client + * should exists. Request an instance via client.getContextMenu(); + * + * @param cli + * to be set as an owner of menu + */ + public VContextMenu() { + super(true, false, true); + setWidget(menu); + setStyleName("i-contextmenu"); + } + + /** + * Sets the element from which to build menu + * + * @param ao + */ + public void setActionOwner(ActionOwner ao) { + actionOwner = ao; + } + + /** + * Shows context menu at given location. + * + * @param left + * @param top + */ + public void showAt(int left, int top) { + this.left = left; + this.top = top; + menu.clearItems(); + final Action[] actions = actionOwner.getActions(); + for (int i = 0; i < actions.length; i++) { + final Action a = actions[i]; + menu.addItem(new MenuItem(a.getHTML(), true, a)); + } + + setPopupPositionAndShow(new PositionCallback() { + public void setPosition(int offsetWidth, int offsetHeight) { + // mac FF gets bad width due GWT popups overflow hacks, + // re-determine width + offsetWidth = menu.getOffsetWidth(); + int left = VContextMenu.this.left; + int top = VContextMenu.this.top; + if (offsetWidth + left > Window.getClientWidth()) { + left = left - offsetWidth; + if (left < 0) { + left = 0; + } + } + if (offsetHeight + top > Window.getClientHeight()) { + top = top - offsetHeight; + if (top < 0) { + top = 0; + } + } + setPopupPosition(left, top); + } + }); + } + + public void showAt(ActionOwner ao, int left, int top) { + setActionOwner(ao); + showAt(left, top); + } + + /** + * Extend standard Gwt MenuBar to set proper settings and to override + * onPopupClosed method so that PopupPanel gets closed. + */ + class CMenuBar extends MenuBar { + public CMenuBar() { + super(true); + } + + @Override + public void onPopupClosed(PopupPanel sender, boolean autoClosed) { + super.onPopupClosed(sender, autoClosed); + hide(); + } + + /* + * public void onBrowserEvent(Event event) { // Remove current selection + * when mouse leaves if (DOM.eventGetType(event) == Event.ONMOUSEOUT) { + * Element to = DOM.eventGetToElement(event); if + * (!DOM.isOrHasChild(getElement(), to)) { DOM.setElementProperty( + * super.getSelectedItem().getElement(), "className", + * super.getSelectedItem().getStylePrimaryName()); } } + * + * super.onBrowserEvent(event); } + */ + } + + public Element getSubPartElement(String subPart) { + int index = Integer.parseInt(subPart.substring(6)); + // ApplicationConnection.getConsole().log( + // "Searching element for selection index " + index); + Element wrapperdiv = menu.getElement(); + com.google.gwt.dom.client.TableSectionElement tBody = (TableSectionElement) wrapperdiv + .getFirstChildElement().getFirstChildElement(); + TableRowElement item = tBody.getRows().getItem(index); + com.google.gwt.dom.client.Element clickableDivElement = item + .getFirstChildElement().getFirstChildElement(); + return clickableDivElement.cast(); + } + + public String getSubPartName(Element subElement) { + if (getElement().isOrHasChild(subElement)) { + com.google.gwt.dom.client.Element e = subElement; + { + while (e != null && !e.getTagName().toLowerCase().equals("tr")) { + e = e.getParentElement(); + // ApplicationConnection.getConsole().log("Found row"); + } + } + com.google.gwt.dom.client.TableSectionElement parentElement = (TableSectionElement) e + .getParentElement(); + NodeList rows = parentElement.getRows(); + for (int i = 0; i < rows.getLength(); i++) { + if (rows.getItem(i) == e) { + // ApplicationConnection.getConsole().log( + // "Found index for row" + 1); + return "option" + i; + } + } + return null; + } else { + return null; + } + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VCustomComponent.java b/src/com/vaadin/terminal/gwt/client/ui/VCustomComponent.java new file mode 100644 index 0000000000..117c67e6ce --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VCustomComponent.java @@ -0,0 +1,153 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.Set; + +import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.DeferredCommand; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.Container; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.RenderSpace; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +public class VCustomComponent extends SimplePanel implements Container { + + private static final String CLASSNAME = "i-customcomponent"; + private String height; + private ApplicationConnection client; + private boolean rendering; + private String width; + private RenderSpace renderSpace = new RenderSpace(); + + public VCustomComponent() { + super(); + setStyleName(CLASSNAME); + } + + public void updateFromUIDL(UIDL uidl, final ApplicationConnection client) { + rendering = true; + if (client.updateComponent(this, uidl, true)) { + rendering = false; + return; + } + this.client = client; + + final UIDL child = uidl.getChildUIDL(0); + if (child != null) { + final Paintable p = client.getPaintable(child); + if (p != getWidget()) { + if (getWidget() != null) { + client.unregisterPaintable((Paintable) getWidget()); + clear(); + } + setWidget((Widget) p); + } + p.updateFromUIDL(child, client); + } + + boolean updateDynamicSize = updateDynamicSize(); + if (updateDynamicSize) { + DeferredCommand.addCommand(new Command() { + public void execute() { + // FIXME deferred relative size update needed to fix some + // scrollbar issues in sampler. This must be the wrong way + // to do it. Might be that some other component is broken. + client.handleComponentRelativeSize(VCustomComponent.this); + + } + }); + } + + renderSpace.setWidth(getElement().getOffsetWidth()); + renderSpace.setHeight(getElement().getOffsetHeight()); + + rendering = false; + } + + private boolean updateDynamicSize() { + boolean updated = false; + if (isDynamicWidth()) { + int childWidth = Util.getRequiredWidth(getWidget()); + getElement().getStyle().setPropertyPx("width", childWidth); + updated = true; + } + if (isDynamicHeight()) { + int childHeight = Util.getRequiredHeight(getWidget()); + getElement().getStyle().setPropertyPx("height", childHeight); + updated = true; + } + + return updated; + } + + private boolean isDynamicWidth() { + return width == null || width.equals(""); + } + + private boolean isDynamicHeight() { + return height == null || height.equals(""); + } + + public boolean hasChildComponent(Widget component) { + if (getWidget() == component) { + return true; + } else { + return false; + } + } + + public void replaceChildComponent(Widget oldComponent, Widget newComponent) { + if (hasChildComponent(oldComponent)) { + clear(); + setWidget(newComponent); + } else { + throw new IllegalStateException(); + } + } + + public void updateCaption(Paintable component, UIDL uidl) { + // NOP, custom component dont render composition roots caption + } + + public boolean requestLayout(Set child) { + return !updateDynamicSize(); + } + + public RenderSpace getAllocatedSpace(Widget child) { + return renderSpace; + } + + @Override + public void setHeight(String height) { + super.setHeight(height); + renderSpace.setHeight(getElement().getOffsetHeight()); + + if (!height.equals(this.height)) { + this.height = height; + if (!rendering) { + client.runDescendentsLayout(this); + } + } + } + + @Override + public void setWidth(String width) { + super.setWidth(width); + renderSpace.setWidth(getElement().getOffsetWidth()); + + if (!width.equals(this.width)) { + this.width = width; + if (!rendering) { + client.runDescendentsLayout(this); + } + } + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VCustomLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VCustomLayout.java new file mode 100644 index 0000000000..5078accb52 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VCustomLayout.java @@ -0,0 +1,644 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import com.google.gwt.dom.client.ImageElement; +import com.google.gwt.dom.client.NodeList; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.ComplexPanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.Container; +import com.vaadin.terminal.gwt.client.ContainerResizedListener; +import com.vaadin.terminal.gwt.client.VCaption; +import com.vaadin.terminal.gwt.client.VCaptionWrapper; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.RenderSpace; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; +import com.vaadin.terminal.gwt.client.RenderInformation.FloatSize; + +/** + * Custom Layout implements complex layout defined with HTML template. + * + * @author IT Mill + * + */ +public class VCustomLayout extends ComplexPanel implements Paintable, + Container, ContainerResizedListener { + + public static final String CLASSNAME = "i-customlayout"; + + /** Location-name to containing element in DOM map */ + private final HashMap locationToElement = new HashMap(); + + /** Location-name to contained widget map */ + private final HashMap locationToWidget = new HashMap(); + + /** Widget to captionwrapper map */ + private final HashMap widgetToCaptionWrapper = new HashMap(); + + /** Name of the currently rendered style */ + String currentTemplateName; + + /** Unexecuted scripts loaded from the template */ + private String scripts = ""; + + /** Paintable ID of this paintable */ + private String pid; + + private ApplicationConnection client; + + /** Has the template been loaded from contents passed in UIDL **/ + private boolean hasTemplateContents = false; + + private Element elementWithNativeResizeFunction; + + private String height = ""; + + private String width = ""; + + private HashMap locationToExtraSize = new HashMap(); + + public VCustomLayout() { + setElement(DOM.createDiv()); + // Clear any unwanted styling + DOM.setStyleAttribute(getElement(), "border", "none"); + DOM.setStyleAttribute(getElement(), "margin", "0"); + DOM.setStyleAttribute(getElement(), "padding", "0"); + + if (BrowserInfo.get().isIE()) { + DOM.setStyleAttribute(getElement(), "position", "relative"); + } + + setStyleName(CLASSNAME); + } + + /** + * Sets widget to given location. + * + * If location already contains a widget it will be removed. + * + * @param widget + * Widget to be set into location. + * @param location + * location name where widget will be added + * + * @throws IllegalArgumentException + * if no such location is found in the layout. + */ + public void setWidget(Widget widget, String location) { + + if (widget == null) { + return; + } + + // If no given location is found in the layout, and exception is throws + Element elem = (Element) locationToElement.get(location); + if (elem == null && hasTemplate()) { + throw new IllegalArgumentException("No location " + location + + " found"); + } + + // Get previous widget + final Widget previous = locationToWidget.get(location); + // NOP if given widget already exists in this location + if (previous == widget) { + return; + } + + if (previous != null) { + remove(previous); + } + + // if template is missing add element in order + if (!hasTemplate()) { + elem = getElement(); + } + + // Add widget to location + super.add(widget, elem); + locationToWidget.put(location, widget); + } + + /** Update the layout from UIDL */ + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + this.client = client; + // ApplicationConnection manages generic component features + if (client.updateComponent(this, uidl, true)) { + return; + } + + pid = uidl.getId(); + if (!hasTemplate()) { + // Update HTML template only once + initializeHTML(uidl, client); + } + + // Evaluate scripts + eval(scripts); + scripts = null; + + iLayout(); + // TODO Check if this is needed + client.runDescendentsLayout(this); + + Set oldWidgets = new HashSet(); + oldWidgets.addAll(locationToWidget.values()); + + // For all contained widgets + for (final Iterator i = uidl.getChildIterator(); i.hasNext();) { + final UIDL uidlForChild = (UIDL) i.next(); + if (uidlForChild.getTag().equals("location")) { + final String location = uidlForChild.getStringAttribute("name"); + final Paintable child = client.getPaintable(uidlForChild + .getChildUIDL(0)); + try { + setWidget((Widget) child, location); + child.updateFromUIDL(uidlForChild.getChildUIDL(0), client); + } catch (final IllegalArgumentException e) { + // If no location is found, this component is not visible + } + oldWidgets.remove(child); + } + } + for (Iterator iterator = oldWidgets.iterator(); iterator.hasNext();) { + Widget oldWidget = (Widget) iterator.next(); + if (oldWidget.isAttached()) { + // slot of this widget is emptied, remove it + remove(oldWidget); + } + } + + iLayout(); + // TODO Check if this is needed + client.runDescendentsLayout(this); + + } + + /** Initialize HTML-layout. */ + private void initializeHTML(UIDL uidl, ApplicationConnection client) { + + final String newTemplateContents = uidl + .getStringAttribute("templateContents"); + final String newTemplate = uidl.getStringAttribute("template"); + + currentTemplateName = null; + hasTemplateContents = false; + + String template = ""; + if (newTemplate != null) { + // Get the HTML-template from client + template = client.getResource("layouts/" + newTemplate + ".html"); + if (template == null) { + template = "Layout file layouts/" + + newTemplate + + ".html is missing. Components will be drawn for debug purposes."; + } else { + currentTemplateName = newTemplate; + } + } else { + hasTemplateContents = true; + template = newTemplateContents; + } + + // Connect body of the template to DOM + template = extractBodyAndScriptsFromTemplate(template); + + // TODO prefix img src:s here with a regeps, cannot work further with IE + + String themeUri = client.getThemeUri(); + String relImgPrefix = themeUri + "/layouts/"; + + // prefix all relative image elements to point to theme dir with a + // regexp search + template = template.replaceAll( + "<((?:img)|(?:IMG))\\s([^>]*)src=\"((?![a-z]+:)[^/][^\"]+)\"", + "<$1 $2src=\"" + relImgPrefix + "$3\""); + // also support src attributes without quotes + template = template + .replaceAll( + "<((?:img)|(?:IMG))\\s([^>]*)src=[^\"]((?![a-z]+:)[^/][^ />]+)[ />]", + "<$1 $2src=\"" + relImgPrefix + "$3\""); + // also prefix relative style="...url(...)..." + template = template + .replaceAll( + "(<[^>]+style=\"[^\"]*url\\()((?![a-z]+:)[^/][^\"]+)(\\)[^>]*>)", + "$1 " + relImgPrefix + "$2 $3"); + + getElement().setInnerHTML(template); + + // Remap locations to elements + locationToElement.clear(); + scanForLocations(getElement()); + + initImgElements(); + + elementWithNativeResizeFunction = DOM.getFirstChild(getElement()); + if (elementWithNativeResizeFunction == null) { + elementWithNativeResizeFunction = getElement(); + } + publishResizedFunction(elementWithNativeResizeFunction); + + } + + private native boolean uriEndsWithSlash() + /*-{ + var path = $wnd.location.pathname; + if(path.charAt(path.length - 1) == "/") + return true; + return false; + }-*/; + + private boolean hasTemplate() { + if (currentTemplateName == null && !hasTemplateContents) { + return false; + } else { + return true; + } + } + + /** Collect locations from template */ + private void scanForLocations(Element elem) { + + final String location = elem.getAttribute("location"); + if (!"".equals(location)) { + locationToElement.put(location, elem); + elem.setInnerHTML(""); + int x = Util.measureHorizontalPaddingAndBorder(elem, 0); + int y = Util.measureVerticalPaddingAndBorder(elem, 0); + + FloatSize fs = new FloatSize(x, y); + + locationToExtraSize.put(location, fs); + + } else { + final int len = DOM.getChildCount(elem); + for (int i = 0; i < len; i++) { + scanForLocations(DOM.getChild(elem, i)); + } + } + } + + /** Evaluate given script in browser document */ + private static native void eval(String script) + /*-{ + try { + if (script != null) + eval("{ var document = $doc; var window = $wnd; "+ script + "}"); + } catch (e) { + } + }-*/; + + /** + * Img elements needs some special handling in custom layout. Img elements + * will get their onload events sunk. This way custom layout can notify + * parent about possible size change. + */ + private void initImgElements() { + NodeList nodeList = getElement() + .getElementsByTagName("IMG"); + for (int i = 0; i < nodeList.getLength(); i++) { + com.google.gwt.dom.client.ImageElement img = (ImageElement) nodeList + .getItem(i); + DOM.sinkEvents((Element) img.cast(), Event.ONLOAD); + } + } + + /** + * Extract body part and script tags from raw html-template. + * + * Saves contents of all script-tags to private property: scripts. Returns + * contents of the body part for the html without script-tags. Also replaces + * all _UID_ tags with an unique id-string. + * + * @param html + * Original HTML-template received from server + * @return html that is used to create the HTMLPanel. + */ + private String extractBodyAndScriptsFromTemplate(String html) { + + // Replace UID:s + html = html.replaceAll("_UID_", pid + "__"); + + // Exctract script-tags + scripts = ""; + int endOfPrevScript = 0; + int nextPosToCheck = 0; + String lc = html.toLowerCase(); + String res = ""; + int scriptStart = lc.indexOf(" 0) { + res += html.substring(endOfPrevScript, scriptStart); + scriptStart = lc.indexOf(">", scriptStart); + final int j = lc.indexOf("", scriptStart); + scripts += html.substring(scriptStart + 1, j) + ";"; + nextPosToCheck = endOfPrevScript = j + "".length(); + scriptStart = lc.indexOf("", startOfBody) + 1; + final int endOfBody = lc.indexOf("", startOfBody); + if (endOfBody > startOfBody) { + res = html.substring(startOfBody, endOfBody); + } else { + res = html.substring(startOfBody); + } + } + + return res; + } + + /** Replace child components */ + public void replaceChildComponent(Widget from, Widget to) { + final String location = getLocation(from); + if (location == null) { + throw new IllegalArgumentException(); + } + setWidget(to, location); + } + + /** Does this layout contain given child */ + public boolean hasChildComponent(Widget component) { + return locationToWidget.containsValue(component); + } + + /** Update caption for given widget */ + public void updateCaption(Paintable component, UIDL uidl) { + VCaptionWrapper wrapper = (VCaptionWrapper) widgetToCaptionWrapper + .get(component); + if (VCaption.isNeeded(uidl)) { + if (wrapper == null) { + final String loc = getLocation((Widget) component); + super.remove((Widget) component); + wrapper = new VCaptionWrapper(component, client); + super.add(wrapper, (Element) locationToElement.get(loc)); + widgetToCaptionWrapper.put(component, wrapper); + } + wrapper.updateCaption(uidl); + } else { + if (wrapper != null) { + final String loc = getLocation((Widget) component); + super.remove(wrapper); + super.add((Widget) wrapper.getPaintable(), + (Element) locationToElement.get(loc)); + widgetToCaptionWrapper.remove(component); + } + } + } + + /** Get the location of an widget */ + public String getLocation(Widget w) { + for (final Iterator i = locationToWidget.keySet().iterator(); i + .hasNext();) { + final String location = (String) i.next(); + if (locationToWidget.get(location) == w) { + return location; + } + } + return null; + } + + /** Removes given widget from the layout */ + @Override + public boolean remove(Widget w) { + client.unregisterPaintable((Paintable) w); + final String location = getLocation(w); + if (location != null) { + locationToWidget.remove(location); + } + final VCaptionWrapper cw = (VCaptionWrapper) widgetToCaptionWrapper + .get(w); + if (cw != null) { + widgetToCaptionWrapper.remove(w); + return super.remove(cw); + } else if (w != null) { + return super.remove(w); + } + return false; + } + + /** Adding widget without specifying location is not supported */ + @Override + public void add(Widget w) { + throw new UnsupportedOperationException(); + } + + /** Clear all widgets from the layout */ + @Override + public void clear() { + super.clear(); + locationToWidget.clear(); + widgetToCaptionWrapper.clear(); + } + + public void iLayout() { + iLayoutJS(DOM.getFirstChild(getElement())); + } + + /** + * This method is published to JS side with the same name into first DOM + * node of custom layout. This way if one implements some resizeable + * containers in custom layout he/she can notify children after resize. + */ + public void notifyChildrenOfSizeChange() { + client.runDescendentsLayout(this); + } + + @Override + public void onDetach() { + super.onDetach(); + detachResizedFunction(elementWithNativeResizeFunction); + } + + private native void detachResizedFunction(Element element) + /*-{ + element.notifyChildrenOfSizeChange = null; + }-*/; + + private native void publishResizedFunction(Element element) + /*-{ + var self = this; + element.notifyChildrenOfSizeChange = function() { + self.@com.vaadin.terminal.gwt.client.ui.VCustomLayout::notifyChildrenOfSizeChange()(); + }; + }-*/; + + /** + * In custom layout one may want to run layout functions made with + * JavaScript. This function tests if one exists (with name "iLayoutJS" in + * layouts first DOM node) and runs et. Return value is used to determine if + * children needs to be notified of size changes. + * + * Note! When implementing a JS layout function you most likely want to call + * notifyChildrenOfSizeChange() function on your custom layouts main + * element. That method is used to control whether child components layout + * functions are to be run. + * + * @param el + * @return true if layout function exists and was run successfully, else + * false. + */ + private native boolean iLayoutJS(Element el) + /*-{ + if(el && el.iLayoutJS) { + try { + el.iLayoutJS(); + return true; + } catch (e) { + return false; + } + } else { + return false; + } + }-*/; + + public boolean requestLayout(Set child) { + updateRelativeSizedComponents(true, true); + + if (width.equals("") || height.equals("")) { + /* Automatically propagated upwards if the size can change */ + return false; + } + + return true; + } + + public RenderSpace getAllocatedSpace(Widget child) { + com.google.gwt.dom.client.Element pe = child.getElement() + .getParentElement(); + + FloatSize extra = locationToExtraSize.get(getLocation(child)); + return new RenderSpace(pe.getOffsetWidth() - (int) extra.getWidth(), pe + .getOffsetHeight() + - (int) extra.getHeight(), Util.mayHaveScrollBars(pe)); + } + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + if (event.getTypeInt() == Event.ONLOAD) { + Util.notifyParentOfSizeChange(this, true); + event.cancelBubble(true); + } + } + + @Override + public void setHeight(String height) { + if (this.height.equals(height)) { + return; + } + + boolean shrinking = true; + if (isLarger(height, this.height)) { + shrinking = false; + } + + this.height = height; + super.setHeight(height); + + /* + * If the height shrinks we must remove all components with relative + * height from the DOM, update their height when they do not affect the + * available space and finally restore them to the original state + */ + if (shrinking) { + updateRelativeSizedComponents(false, true); + } + } + + @Override + public void setWidth(String width) { + if (this.width.equals(width)) { + return; + } + + boolean shrinking = true; + if (isLarger(width, this.width)) { + shrinking = false; + } + + super.setWidth(width); + this.width = width; + + /* + * If the width shrinks we must remove all components with relative + * width from the DOM, update their width when they do not affect the + * available space and finally restore them to the original state + */ + if (shrinking) { + updateRelativeSizedComponents(true, false); + } + } + + private void updateRelativeSizedComponents(boolean relativeWidth, + boolean relativeHeight) { + + Set relativeSizeWidgets = new HashSet(); + + for (Widget widget : locationToWidget.values()) { + FloatSize relativeSize = client.getRelativeSize(widget); + if (relativeSize != null) { + if ((relativeWidth && (relativeSize.getWidth() >= 0.0f)) + || (relativeHeight && (relativeSize.getHeight() >= 0.0f))) { + + relativeSizeWidgets.add(widget); + widget.getElement().getStyle().setProperty("position", + "absolute"); + } + } + } + + for (Widget widget : relativeSizeWidgets) { + client.handleComponentRelativeSize(widget); + widget.getElement().getStyle().setProperty("position", ""); + } + } + + /** + * Compares newSize with currentSize and returns true if it is clear that + * newSize is larger than currentSize. Returns false if newSize is smaller + * or if it is unclear which one is smaller. + * + * @param newSize + * @param currentSize + * @return + */ + private boolean isLarger(String newSize, String currentSize) { + if (newSize.equals("") || currentSize.equals("")) { + return false; + } + + if (!newSize.endsWith("px") || !currentSize.endsWith("px")) { + return false; + } + + int newSizePx = Integer.parseInt(newSize.substring(0, + newSize.length() - 2)); + int currentSizePx = Integer.parseInt(currentSize.substring(0, + currentSize.length() - 2)); + + boolean larger = newSizePx > currentSizePx; + return larger; + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VDateField.java b/src/com/vaadin/terminal/gwt/client/ui/VDateField.java new file mode 100644 index 0000000000..b9b2c3b942 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VDateField.java @@ -0,0 +1,233 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.Date; + +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.FlowPanel; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.ClientExceptionHandler; +import com.vaadin.terminal.gwt.client.DateTimeService; +import com.vaadin.terminal.gwt.client.VTooltip; +import com.vaadin.terminal.gwt.client.LocaleNotLoadedException; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; + +public class VDateField extends FlowPanel implements Paintable, Field { + + public static final String CLASSNAME = "i-datefield"; + + protected String id; + + protected ApplicationConnection client; + + protected boolean immediate; + + public static final int RESOLUTION_YEAR = 0; + public static final int RESOLUTION_MONTH = 1; + public static final int RESOLUTION_DAY = 2; + public static final int RESOLUTION_HOUR = 3; + public static final int RESOLUTION_MIN = 4; + public static final int RESOLUTION_SEC = 5; + public static final int RESOLUTION_MSEC = 6; + + protected int currentResolution = RESOLUTION_YEAR; + + protected String currentLocale; + + protected boolean readonly; + + protected boolean enabled; + + protected Date date = null; + // e.g when paging a calendar, before actually selecting + protected Date showingDate = new Date(); + + protected DateTimeService dts; + + public VDateField() { + setStyleName(CLASSNAME); + dts = new DateTimeService(); + sinkEvents(VTooltip.TOOLTIP_EVENTS); + } + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + if (client != null) { + client.handleTooltipEvent(event, this); + } + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + // Ensure correct implementation and let layout manage caption + if (client.updateComponent(this, uidl, true)) { + return; + } + + // Save details + this.client = client; + id = uidl.getId(); + immediate = uidl.getBooleanAttribute("immediate"); + + readonly = uidl.getBooleanAttribute("readonly"); + enabled = !uidl.getBooleanAttribute("disabled"); + + if (uidl.hasAttribute("locale")) { + final String locale = uidl.getStringAttribute("locale"); + try { + dts.setLocale(locale); + currentLocale = locale; + } catch (final LocaleNotLoadedException e) { + currentLocale = dts.getLocale(); + ClientExceptionHandler.displayError( + "Tried to use an unloaded locale \"" + locale + + "\". Using default locale (" + currentLocale + + ").", e); + } + } + + int newResolution; + if (uidl.hasVariable("msec")) { + newResolution = RESOLUTION_MSEC; + } else if (uidl.hasVariable("sec")) { + newResolution = RESOLUTION_SEC; + } else if (uidl.hasVariable("min")) { + newResolution = RESOLUTION_MIN; + } else if (uidl.hasVariable("hour")) { + newResolution = RESOLUTION_HOUR; + } else if (uidl.hasVariable("day")) { + newResolution = RESOLUTION_DAY; + } else if (uidl.hasVariable("month")) { + newResolution = RESOLUTION_MONTH; + } else { + newResolution = RESOLUTION_YEAR; + } + + currentResolution = newResolution; + + final int year = uidl.getIntVariable("year"); + final int month = (currentResolution >= RESOLUTION_MONTH) ? uidl + .getIntVariable("month") : -1; + final int day = (currentResolution >= RESOLUTION_DAY) ? uidl + .getIntVariable("day") : -1; + final int hour = (currentResolution >= RESOLUTION_HOUR) ? uidl + .getIntVariable("hour") : 0; + final int min = (currentResolution >= RESOLUTION_MIN) ? uidl + .getIntVariable("min") : 0; + final int sec = (currentResolution >= RESOLUTION_SEC) ? uidl + .getIntVariable("sec") : 0; + final int msec = (currentResolution >= RESOLUTION_MSEC) ? uidl + .getIntVariable("msec") : 0; + + // Construct new date for this datefield (only if not null) + if (year > -1) { + date = new Date((long) getTime(year, month, day, hour, min, sec, + msec)); + showingDate.setTime(date.getTime()); + } else { + date = null; + showingDate = new Date(); + } + + } + + /* + * We need this redundant native function because Java's Date object doesn't + * have a setMilliseconds method. + */ + private static native double getTime(int y, int m, int d, int h, int mi, + int s, int ms) + /*-{ + try { + var date = new Date(2000,1,1,1); // don't use current date here + if(y && y >= 0) date.setFullYear(y); + if(m && m >= 1) date.setMonth(m-1); + if(d && d >= 0) date.setDate(d); + if(h >= 0) date.setHours(h); + if(mi >= 0) date.setMinutes(mi); + if(s >= 0) date.setSeconds(s); + if(ms >= 0) date.setMilliseconds(ms); + return date.getTime(); + } catch (e) { + // TODO print some error message on the console + //console.log(e); + return (new Date()).getTime(); + } + }-*/; + + public int getMilliseconds() { + return (int) (date.getTime() - date.getTime() / 1000 * 1000); + } + + public void setMilliseconds(int ms) { + date.setTime(date.getTime() / 1000 * 1000 + ms); + } + + public int getShowingMilliseconds() { + return (int) (showingDate.getTime() - showingDate.getTime() / 1000 * 1000); + } + + public void setShowingMilliseconds(int ms) { + showingDate.setTime(showingDate.getTime() / 1000 * 1000 + ms); + } + + public int getCurrentResolution() { + return currentResolution; + } + + public void setCurrentResolution(int currentResolution) { + this.currentResolution = currentResolution; + } + + public String getCurrentLocale() { + return currentLocale; + } + + public void setCurrentLocale(String currentLocale) { + this.currentLocale = currentLocale; + } + + public Date getCurrentDate() { + return date; + } + + public void setCurrentDate(Date date) { + this.date = date; + } + + public Date getShowingDate() { + return showingDate; + } + + public void setShowingDate(Date date) { + showingDate = date; + } + + public boolean isImmediate() { + return immediate; + } + + public boolean isReadonly() { + return readonly; + } + + public boolean isEnabled() { + return enabled; + } + + public DateTimeService getDateTimeService() { + return dts; + } + + public String getId() { + return id; + } + + public ApplicationConnection getClient() { + return client; + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VDateFieldCalendar.java b/src/com/vaadin/terminal/gwt/client/ui/VDateFieldCalendar.java new file mode 100644 index 0000000000..fec6756da6 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VDateFieldCalendar.java @@ -0,0 +1,26 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.UIDL; + +public class VDateFieldCalendar extends VDateField { + + private final VCalendarPanel date; + + public VDateFieldCalendar() { + super(); + date = new VCalendarPanel(this); + add(date); + } + + @Override + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + super.updateFromUIDL(uidl, client); + date.updateCalendar(); + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java b/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java new file mode 100644 index 0000000000..8d56821b8d --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java @@ -0,0 +1,225 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.Iterator; + +import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.Node; +import com.google.gwt.dom.client.NodeList; +import com.google.gwt.dom.client.ObjectElement; +import com.google.gwt.dom.client.Style; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.HTML; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +public class VEmbedded extends HTML implements Paintable { + private static String CLASSNAME = "i-embedded"; + + private String height; + private String width; + private Element browserElement; + + private ApplicationConnection client; + + public VEmbedded() { + setStyleName(CLASSNAME); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + if (client.updateComponent(this, uidl, true)) { + return; + } + this.client = client; + + boolean clearBrowserElement = true; + + if (uidl.hasAttribute("type")) { + final String type = uidl.getStringAttribute("type"); + if (type.equals("image")) { + Element el = null; + boolean created = false; + NodeList nodes = getElement().getChildNodes(); + if (nodes != null && nodes.getLength() == 1) { + Node n = nodes.getItem(0); + if (n.getNodeType() == Node.ELEMENT_NODE) { + Element e = (Element) n; + if (e.getTagName().equals("IMG")) { + el = e; + } + } + } + if (el == null) { + setHTML(""); + el = DOM.createImg(); + created = true; + client.addPngFix(el); + DOM.sinkEvents(el, Event.ONLOAD); + } + + // Set attributes + Style style = el.getStyle(); + String w = uidl.getStringAttribute("width"); + if (w != null) { + style.setProperty("width", w); + } else { + style.setProperty("width", ""); + } + String h = uidl.getStringAttribute("height"); + if (h != null) { + style.setProperty("height", h); + } else { + style.setProperty("height", ""); + } + DOM.setElementProperty(el, "src", getSrc(uidl, client)); + + if (created) { + // insert in dom late + getElement().appendChild(el); + } + + } else if (type.equals("browser")) { + if (browserElement == null) { + setHTML(""); + browserElement = DOM.getFirstChild(getElement()); + } else { + DOM.setElementAttribute(browserElement, "src", getSrc(uidl, + client)); + } + clearBrowserElement = false; + } else { + ApplicationConnection.getConsole().log( + "Unknown Embedded type '" + type + "'"); + } + } else if (uidl.hasAttribute("mimetype")) { + final String mime = uidl.getStringAttribute("mimetype"); + if (mime.equals("application/x-shockwave-flash")) { + setHTML(""); + } else if (mime.equals("image/svg+xml")) { + String data; + if (getParameter("data", uidl) == null) { + data = getSrc(uidl, client); + } else { + data = "data:image/svg+xml," + getParameter("data", uidl); + } + setHTML(""); + ObjectElement obj = Document.get().createObjectElement(); + obj.setType(mime); + obj.setData(data); + if (width != null) { + obj.getStyle().setProperty("width", "100%"); + } + if (height != null) { + obj.getStyle().setProperty("height", "100%"); + } + getElement().appendChild(obj); + + } else { + ApplicationConnection.getConsole().log( + "Unknown Embedded mimetype '" + mime + "'"); + } + } else { + ApplicationConnection.getConsole().log( + "Unknown Embedded; no type or mimetype attribute"); + } + + if (clearBrowserElement) { + browserElement = null; + } + + } + + private static String getParameter(String paramName, UIDL uidl) { + Iterator childIterator = uidl.getChildIterator(); + while (childIterator.hasNext()) { + Object child = childIterator.next(); + if (child instanceof UIDL) { + UIDL childUIDL = (UIDL) child; + if (childUIDL.getTag().equals("embeddedparam") + && childUIDL.getStringAttribute("name").equals( + paramName)) { + return childUIDL.getStringAttribute("value"); + } + + } + } + return null; + } + + /** + * Helper to return translated src-attribute from embedded's UIDL + * + * @param uidl + * @param client + * @return + */ + private String getSrc(UIDL uidl, ApplicationConnection client) { + String url = client.translateToolkitUri(uidl.getStringAttribute("src")); + if (url == null) { + return ""; + } + return url; + } + + @Override + public void setWidth(String width) { + this.width = width; + if (isDynamicHeight()) { + int oldHeight = getOffsetHeight(); + super.setWidth(width); + int newHeight = getOffsetHeight(); + /* + * Must notify parent if the height changes as a result of a width + * change + */ + if (oldHeight != newHeight) { + Util.notifyParentOfSizeChange(this, false); + } + } else { + super.setWidth(width); + } + + } + + private boolean isDynamicHeight() { + return height == null || height.equals(""); + } + + @Override + public void setHeight(String height) { + this.height = height; + super.setHeight(height); + } + + @Override + protected void onDetach() { + // Force browser to fire unload event when component is detached from + // the view (IE doesn't do this automatically) + if (browserElement != null) { + DOM.setElementAttribute(browserElement, "src", "javascript:false"); + } + super.onDetach(); + } + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + if (DOM.eventGetType(event) == Event.ONLOAD) { + Util.notifyParentOfSizeChange(this, true); + } + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java b/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java new file mode 100644 index 0000000000..378986e389 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java @@ -0,0 +1,1059 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.ui.ClickListener; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.FocusListener; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.Image; +import com.google.gwt.user.client.ui.KeyboardListener; +import com.google.gwt.user.client.ui.LoadListener; +import com.google.gwt.user.client.ui.PopupListener; +import com.google.gwt.user.client.ui.PopupPanel; +import com.google.gwt.user.client.ui.TextBox; +import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.user.client.ui.PopupPanel.PositionCallback; +import com.google.gwt.user.client.ui.SuggestOracle.Suggestion; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.Focusable; +import com.vaadin.terminal.gwt.client.VTooltip; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +/** + * + * TODO needs major refactoring (to be extensible etc) + */ +public class VFilterSelect extends Composite implements Paintable, Field, + KeyboardListener, ClickListener, FocusListener, Focusable { + + public class FilterSelectSuggestion implements Suggestion, Command { + + private final String key; + private final String caption; + private String iconUri; + + public FilterSelectSuggestion(UIDL uidl) { + key = uidl.getStringAttribute("key"); + caption = uidl.getStringAttribute("caption"); + if (uidl.hasAttribute("icon")) { + iconUri = client.translateToolkitUri(uidl + .getStringAttribute("icon")); + } + } + + public String getDisplayString() { + final StringBuffer sb = new StringBuffer(); + if (iconUri != null) { + sb.append("\"\""); + } + sb.append("" + Util.escapeHTML(caption) + ""); + return sb.toString(); + } + + public String getReplacementString() { + return caption; + } + + public int getOptionKey() { + return Integer.parseInt(key); + } + + public String getIconUri() { + return iconUri; + } + + public void execute() { + onSuggestionSelected(this); + } + } + + public class SuggestionPopup extends VToolkitOverlay implements + PositionCallback, PopupListener { + + private static final String Z_INDEX = "30000"; + + private final SuggestionMenu menu; + + private final Element up = DOM.createDiv(); + private final Element down = DOM.createDiv(); + private final Element status = DOM.createDiv(); + + private boolean isPagingEnabled = true; + + private long lastAutoClosed; + + private int popupOuterPadding = -1; + + private int topPosition; + + SuggestionPopup() { + super(true, false, true); + menu = new SuggestionMenu(); + setWidget(menu); + setStyleName(CLASSNAME + "-suggestpopup"); + DOM.setStyleAttribute(getElement(), "zIndex", Z_INDEX); + + final Element root = getContainerElement(); + + DOM.setInnerHTML(up, "Prev"); + DOM.sinkEvents(up, Event.ONCLICK); + DOM.setInnerHTML(down, "Next"); + DOM.sinkEvents(down, Event.ONCLICK); + DOM.insertChild(root, up, 0); + DOM.appendChild(root, down); + DOM.appendChild(root, status); + DOM.setElementProperty(status, "className", CLASSNAME + "-status"); + + addPopupListener(this); + } + + public void showSuggestions( + Collection currentSuggestions, + int currentPage, int totalSuggestions) { + + // Add TT anchor point + DOM.setElementProperty(getElement(), "id", + "TOOLKIT_COMBOBOX_OPTIONLIST"); + + menu.setSuggestions(currentSuggestions); + final int x = VFilterSelect.this.getAbsoluteLeft(); + topPosition = tb.getAbsoluteTop(); + topPosition += tb.getOffsetHeight(); + setPopupPosition(x, topPosition); + + final int first = currentPage * PAGELENTH + + (nullSelectionAllowed && currentPage > 0 ? 0 : 1); + final int last = first + currentSuggestions.size() - 1; + final int matches = totalSuggestions + - (nullSelectionAllowed ? 1 : 0); + if (last > 0) { + // nullsel not counted, as requested by user + DOM.setInnerText(status, (matches == 0 ? 0 : first) + + "-" + + ("".equals(lastFilter) && nullSelectionAllowed + && currentPage == 0 ? last - 1 : last) + "/" + + matches); + } else { + DOM.setInnerText(status, ""); + } + // We don't need to show arrows or statusbar if there is only one + // page + if (matches <= PAGELENTH) { + setPagingEnabled(false); + } else { + setPagingEnabled(true); + } + setPrevButtonActive(first > 1); + setNextButtonActive(last < matches); + + // clear previously fixed width + menu.setWidth(""); + DOM.setStyleAttribute(DOM.getFirstChild(menu.getElement()), + "width", ""); + + setPopupPositionAndShow(this); + } + + private void setNextButtonActive(boolean b) { + if (b) { + DOM.sinkEvents(down, Event.ONCLICK); + DOM.setElementProperty(down, "className", CLASSNAME + + "-nextpage"); + } else { + DOM.sinkEvents(down, 0); + DOM.setElementProperty(down, "className", CLASSNAME + + "-nextpage-off"); + } + } + + private void setPrevButtonActive(boolean b) { + if (b) { + DOM.sinkEvents(up, Event.ONCLICK); + DOM + .setElementProperty(up, "className", CLASSNAME + + "-prevpage"); + } else { + DOM.sinkEvents(up, 0); + DOM.setElementProperty(up, "className", CLASSNAME + + "-prevpage-off"); + } + + } + + public void selectNextItem() { + final MenuItem cur = menu.getSelectedItem(); + final int index = 1 + menu.getItems().indexOf(cur); + if (menu.getItems().size() > index) { + final MenuItem newSelectedItem = (MenuItem) menu.getItems() + .get(index); + menu.selectItem(newSelectedItem); + tb.setText(newSelectedItem.getText()); + tb.setSelectionRange(lastFilter.length(), newSelectedItem + .getText().length() + - lastFilter.length()); + + } else if (hasNextPage()) { + lastIndex = index - 1; // save for paging + filterOptions(currentPage + 1, lastFilter); + } + } + + public void selectPrevItem() { + final MenuItem cur = menu.getSelectedItem(); + final int index = -1 + menu.getItems().indexOf(cur); + if (index > -1) { + final MenuItem newSelectedItem = (MenuItem) menu.getItems() + .get(index); + menu.selectItem(newSelectedItem); + tb.setText(newSelectedItem.getText()); + tb.setSelectionRange(lastFilter.length(), newSelectedItem + .getText().length() + - lastFilter.length()); + } else if (index == -1) { + if (currentPage > 0) { + lastIndex = index + 1; // save for paging + filterOptions(currentPage - 1, lastFilter); + } + } else { + final MenuItem newSelectedItem = (MenuItem) menu.getItems() + .get(menu.getItems().size() - 1); + menu.selectItem(newSelectedItem); + tb.setText(newSelectedItem.getText()); + tb.setSelectionRange(lastFilter.length(), newSelectedItem + .getText().length() + - lastFilter.length()); + } + } + + @Override + public void onBrowserEvent(Event event) { + final Element target = DOM.eventGetTarget(event); + if (DOM.compare(target, up) + || DOM.compare(target, DOM.getChild(up, 0))) { + filterOptions(currentPage - 1, lastFilter); + } else if (DOM.compare(target, down) + || DOM.compare(target, DOM.getChild(down, 0))) { + filterOptions(currentPage + 1, lastFilter); + } + tb.setFocus(true); + } + + public void setPagingEnabled(boolean paging) { + if (isPagingEnabled == paging) { + return; + } + if (paging) { + DOM.setStyleAttribute(down, "display", ""); + DOM.setStyleAttribute(up, "display", ""); + DOM.setStyleAttribute(status, "display", ""); + } else { + DOM.setStyleAttribute(down, "display", "none"); + DOM.setStyleAttribute(up, "display", "none"); + DOM.setStyleAttribute(status, "display", "none"); + } + isPagingEnabled = paging; + } + + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.user.client.ui.PopupPanel$PositionCallback#setPosition + * (int, int) + */ + public void setPosition(int offsetWidth, int offsetHeight) { + + int top = -1; + int left = -1; + + // reset menu size and retrieve its "natural" size + menu.setHeight(""); + if (currentPage > 0) { + // fix height to avoid height change when getting to last page + menu.fixHeightTo(PAGELENTH); + } + offsetHeight = getOffsetHeight(); + + final int desiredWidth = getMainWidth(); + int naturalMenuWidth = DOM.getElementPropertyInt(DOM + .getFirstChild(menu.getElement()), "offsetWidth"); + + if (popupOuterPadding == -1) { + popupOuterPadding = Util.measureHorizontalPaddingAndBorder( + getElement(), 2); + } + + if (naturalMenuWidth < desiredWidth) { + menu.setWidth((desiredWidth - popupOuterPadding) + "px"); + DOM.setStyleAttribute(DOM.getFirstChild(menu.getElement()), + "width", "100%"); + naturalMenuWidth = desiredWidth; + } + + if (BrowserInfo.get().isIE()) { + /* + * IE requires us to specify the width for the container + * element. Otherwise it will be 100% wide + */ + int rootWidth = naturalMenuWidth - popupOuterPadding; + DOM.setStyleAttribute(getContainerElement(), "width", rootWidth + + "px"); + } + + if (offsetHeight + getPopupTop() > Window.getClientHeight() + + Window.getScrollTop()) { + // popup on top of input instead + top = getPopupTop() - offsetHeight + - VFilterSelect.this.getOffsetHeight(); + if (top < 0) { + top = 0; + } + } else { + top = getPopupTop(); + /* + * Take popup top margin into account. getPopupTop() returns the + * top value including the margin but the value we give must not + * include the margin. + */ + int topMargin = (top - topPosition); + top -= topMargin; + } + + // fetch real width (mac FF bugs here due GWT popups overflow:auto ) + offsetWidth = DOM.getElementPropertyInt(DOM.getFirstChild(menu + .getElement()), "offsetWidth"); + if (offsetWidth + getPopupLeft() > Window.getClientWidth() + + Window.getScrollLeft()) { + left = VFilterSelect.this.getAbsoluteLeft() + + VFilterSelect.this.getOffsetWidth() + + Window.getScrollLeft() - offsetWidth; + if (left < 0) { + left = 0; + } + } else { + left = getPopupLeft(); + } + setPopupPosition(left, top); + + } + + /** + * @return true if popup was just closed + */ + public boolean isJustClosed() { + final long now = (new Date()).getTime(); + return (lastAutoClosed > 0 && (now - lastAutoClosed) < 200); + } + + public void onPopupClosed(PopupPanel sender, boolean autoClosed) { + if (autoClosed) { + lastAutoClosed = (new Date()).getTime(); + } + } + + /** + * Updates style names in suggestion popup to help theme building. + */ + public void updateStyleNames(UIDL uidl) { + if (uidl.hasAttribute("style")) { + setStyleName(CLASSNAME + "-suggestpopup"); + final String[] styles = uidl.getStringAttribute("style").split( + " "); + for (int i = 0; i < styles.length; i++) { + addStyleDependentName(styles[i]); + } + } + } + + } + + public class SuggestionMenu extends MenuBar { + + SuggestionMenu() { + super(true); + setStyleName(CLASSNAME + "-suggestmenu"); + } + + /** + * Fixes menus height to use same space as full page would use. Needed + * to avoid height changes when quickly "scrolling" to last page + */ + public void fixHeightTo(int pagelenth) { + if (currentSuggestions.size() > 0) { + final int pixels = pagelenth * (getOffsetHeight() - 2) + / currentSuggestions.size(); + setHeight((pixels + 2) + "px"); + } + } + + public void setSuggestions( + Collection suggestions) { + clearItems(); + final Iterator it = suggestions.iterator(); + while (it.hasNext()) { + final FilterSelectSuggestion s = it.next(); + final MenuItem mi = new MenuItem(s.getDisplayString(), true, s); + + com.google.gwt.dom.client.Element child = mi.getElement() + .getFirstChildElement(); + while (child != null) { + if (child.getNodeName().toLowerCase().equals("img")) { + DOM + .sinkEvents((Element) child.cast(), + (DOM.getEventsSunk((Element) child + .cast()) | Event.ONLOAD)); + } + child = child.getNextSiblingElement(); + } + + this.addItem(mi); + if (s == currentSuggestion) { + selectItem(mi); + } + } + } + + public void doSelectedItemAction() { + final MenuItem item = getSelectedItem(); + final String enteredItemValue = tb.getText(); + // check for exact match in menu + int p = getItems().size(); + if (p > 0) { + for (int i = 0; i < p; i++) { + final MenuItem potentialExactMatch = (MenuItem) getItems() + .get(i); + if (potentialExactMatch.getText().equals(enteredItemValue)) { + selectItem(potentialExactMatch); + doItemAction(potentialExactMatch, true); + suggestionPopup.hide(); + return; + } + } + } + if (allowNewItem) { + + if (!prompting && !enteredItemValue.equals(lastNewItemString)) { + /* + * Store last sent new item string to avoid double sends + */ + lastNewItemString = enteredItemValue; + client.updateVariable(paintableId, "newitem", + enteredItemValue, immediate); + } + } else if (item != null + && !"".equals(lastFilter) + && item.getText().toLowerCase().startsWith( + lastFilter.toLowerCase())) { + doItemAction(item, true); + } else { + if (currentSuggestion != null) { + String text = currentSuggestion.getReplacementString(); + /* TODO? + if (text.equals("")) { + addStyleDependentName(CLASSNAME_PROMPT); + tb.setText(inputPrompt); + prompting = true; + } else { + tb.setText(text); + prompting = false; + removeStyleDependentName(CLASSNAME_PROMPT); + } + */ + selectedOptionKey = currentSuggestion.key; + } + } + suggestionPopup.hide(); + } + + @Override + public void onBrowserEvent(Event event) { + if (event.getTypeInt() == Event.ONLOAD) { + if (suggestionPopup.isVisible()) { + setWidth(""); + DOM.setStyleAttribute(DOM.getFirstChild(getElement()), + "width", ""); + suggestionPopup.setPopupPositionAndShow(suggestionPopup); + } + } + super.onBrowserEvent(event); + } + } + + public static final int FILTERINGMODE_OFF = 0; + public static final int FILTERINGMODE_STARTSWITH = 1; + public static final int FILTERINGMODE_CONTAINS = 2; + + private static final String CLASSNAME = "i-filterselect"; + + public static final int PAGELENTH = 10; + + private final FlowPanel panel = new FlowPanel(); + + private final TextBox tb = new TextBox() { + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + if (client != null) { + client.handleTooltipEvent(event, VFilterSelect.this); + } + } + }; + + private final SuggestionPopup suggestionPopup = new SuggestionPopup(); + + private final HTML popupOpener = new HTML(""); + + private final Image selectedItemIcon = new Image(); + + private ApplicationConnection client; + + private String paintableId; + + private int currentPage; + + private final Collection currentSuggestions = new ArrayList(); + + private boolean immediate; + + private String selectedOptionKey; + + private boolean filtering = false; + + private String lastFilter = ""; + private int lastIndex = -1; // last selected index when using arrows + + private FilterSelectSuggestion currentSuggestion; + + private int totalMatches; + private boolean allowNewItem; + private boolean nullSelectionAllowed; + private boolean enabled; + + // shown in unfocused empty field, disappears on focus (e.g "Search here") + private static final String CLASSNAME_PROMPT = "prompt"; + private static final String ATTR_INPUTPROMPT = "prompt"; + private String inputPrompt = ""; + private boolean prompting = false; + + // Set true when popupopened has been clicked. Cleared on each UIDL-update. + // This handles the special case where are not filtering yet and the + // selected value has changed on the server-side. See #2119 + private boolean popupOpenerClicked; + private String width = null; + private int textboxPadding = -1; + private int componentPadding = -1; + private int suggestionPopupMinWidth = 0; + /* + * Stores the last new item string to avoid double submissions. Cleared on + * uidl updates + */ + private String lastNewItemString; + private boolean focused = false; + + public VFilterSelect() { + selectedItemIcon.setVisible(false); + selectedItemIcon.setStyleName("i-icon"); + selectedItemIcon.addLoadListener(new LoadListener() { + public void onError(Widget sender) { + } + + public void onLoad(Widget sender) { + updateRootWidth(); + updateSelectedIconPosition(); + } + }); + + panel.add(selectedItemIcon); + tb.sinkEvents(VTooltip.TOOLTIP_EVENTS); + panel.add(tb); + panel.add(popupOpener); + initWidget(panel); + setStyleName(CLASSNAME); + tb.addKeyboardListener(this); + tb.setStyleName(CLASSNAME + "-input"); + tb.addFocusListener(this); + popupOpener.setStyleName(CLASSNAME + "-button"); + popupOpener.addClickListener(this); + } + + public boolean hasNextPage() { + if (totalMatches > (currentPage + 1) * PAGELENTH) { + return true; + } else { + return false; + } + } + + public void filterOptions(int page) { + filterOptions(page, tb.getText()); + } + + public void filterOptions(int page, String filter) { + if (filter.equals(lastFilter) && currentPage == page) { + if (!suggestionPopup.isAttached()) { + suggestionPopup.showSuggestions(currentSuggestions, + currentPage, totalMatches); + } + return; + } + if (!filter.equals(lastFilter)) { + // we are on subsequent page and text has changed -> reset page + if ("".equals(filter)) { + // let server decide + page = -1; + } else { + page = 0; + } + } + + filtering = true; + client.updateVariable(paintableId, "filter", filter, false); + client.updateVariable(paintableId, "page", page, true); + lastFilter = filter; + currentPage = page; + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + paintableId = uidl.getId(); + this.client = client; + + boolean readonly = uidl.hasAttribute("readonly"); + boolean disabled = uidl.hasAttribute("disabled"); + + if (disabled || readonly) { + tb.setEnabled(false); + enabled = false; + } else { + tb.setEnabled(true); + enabled = true; + } + + if (client.updateComponent(this, uidl, true)) { + return; + } + + // not a FocusWidget -> needs own tabindex handling + if (uidl.hasAttribute("tabindex")) { + tb.setTabIndex(uidl.getIntAttribute("tabindex")); + } + + immediate = uidl.hasAttribute("immediate"); + + nullSelectionAllowed = uidl.hasAttribute("nullselect"); + + currentPage = uidl.getIntVariable("page"); + + if (uidl.hasAttribute(ATTR_INPUTPROMPT)) { + // input prompt changed from server + inputPrompt = uidl.getStringAttribute(ATTR_INPUTPROMPT); + } else { + inputPrompt = ""; + } + + suggestionPopup.setPagingEnabled(true); + suggestionPopup.updateStyleNames(uidl); + + allowNewItem = uidl.hasAttribute("allownewitem"); + lastNewItemString = null; + + currentSuggestions.clear(); + final UIDL options = uidl.getChildUIDL(0); + totalMatches = uidl.getIntAttribute("totalMatches"); + + String captions = inputPrompt; + + for (final Iterator i = options.getChildIterator(); i.hasNext();) { + final UIDL optionUidl = (UIDL) i.next(); + final FilterSelectSuggestion suggestion = new FilterSelectSuggestion( + optionUidl); + currentSuggestions.add(suggestion); + if (optionUidl.hasAttribute("selected")) { + if (!filtering || popupOpenerClicked) { + tb.setText(suggestion.getReplacementString()); + selectedOptionKey = "" + suggestion.getOptionKey(); + } + currentSuggestion = suggestion; + setSelectedItemIcon(suggestion.getIconUri()); + } + + // Collect captions so we can calculate minimum width for textarea + if (captions.length() > 0) { + captions += "|"; + } + captions += suggestion.getReplacementString(); + } + + if ((!filtering || popupOpenerClicked) && uidl.hasVariable("selected") + && uidl.getStringArrayVariable("selected").length == 0) { + // select nulled + if (!filtering || !popupOpenerClicked) { + setPromptingOn(); + } + selectedOptionKey = null; + } + + if (filtering + && lastFilter.toLowerCase().equals( + uidl.getStringVariable("filter"))) { + suggestionPopup.showSuggestions(currentSuggestions, currentPage, + totalMatches); + filtering = false; + if (!popupOpenerClicked && lastIndex != -1) { + // we're paging w/ arrows + if (lastIndex == 0) { + // going up, select last item + int lastItem = PAGELENTH - 1; + List items = suggestionPopup.menu.getItems(); + /* + * The first page can contain less than 10 items if the null + * selection item is filtered away + */ + if (lastItem >= items.size()) { + lastItem = items.size() - 1; + } + suggestionPopup.menu.selectItem((MenuItem) items + .get(lastItem)); + } else { + // going down, select first item + suggestionPopup.menu + .selectItem((MenuItem) suggestionPopup.menu + .getItems().get(0)); + } + lastIndex = -1; // reset + } + } + + // Calculate minumum textarea width + suggestionPopupMinWidth = minWidth(captions); + + popupOpenerClicked = false; + + updateRootWidth(); + } + + private void setPromptingOn() { + prompting = true; + addStyleDependentName(CLASSNAME_PROMPT); + tb.setText(inputPrompt); + } + + private void setPromptingOff(String text) { + tb.setText(text); + prompting = false; + removeStyleDependentName(CLASSNAME_PROMPT); + } + + public void onSuggestionSelected(FilterSelectSuggestion suggestion) { + currentSuggestion = suggestion; + String newKey; + if (suggestion.key.equals("")) { + // "nullselection" + newKey = ""; + } else { + // normal selection + newKey = String.valueOf(suggestion.getOptionKey()); + } + + String text = suggestion.getReplacementString(); + if ("".equals(newKey) && !focused) { + setPromptingOn(); + } else { + setPromptingOff(text); + } + setSelectedItemIcon(suggestion.getIconUri()); + if (!newKey.equals(selectedOptionKey)) { + selectedOptionKey = newKey; + client.updateVariable(paintableId, "selected", + new String[] { selectedOptionKey }, immediate); + // currentPage = -1; // forget the page + } + suggestionPopup.hide(); + } + + private void setSelectedItemIcon(String iconUri) { + if (iconUri == null) { + selectedItemIcon.setVisible(false); + updateRootWidth(); + } else { + selectedItemIcon.setUrl(iconUri); + selectedItemIcon.setVisible(true); + updateRootWidth(); + updateSelectedIconPosition(); + } + } + + private void updateSelectedIconPosition() { + // Position icon vertically to middle + int availableHeight = getOffsetHeight(); + int iconHeight = Util.getRequiredHeight(selectedItemIcon); + int marginTop = (availableHeight - iconHeight) / 2; + DOM.setStyleAttribute(selectedItemIcon.getElement(), "marginTop", + marginTop + "px"); + } + + public void onKeyDown(Widget sender, char keyCode, int modifiers) { + if (enabled && suggestionPopup.isAttached()) { + switch (keyCode) { + case KeyboardListener.KEY_DOWN: + suggestionPopup.selectNextItem(); + DOM.eventPreventDefault(DOM.eventGetCurrentEvent()); + break; + case KeyboardListener.KEY_UP: + suggestionPopup.selectPrevItem(); + DOM.eventPreventDefault(DOM.eventGetCurrentEvent()); + break; + case KeyboardListener.KEY_PAGEDOWN: + if (hasNextPage()) { + filterOptions(currentPage + 1, lastFilter); + } + break; + case KeyboardListener.KEY_PAGEUP: + if (currentPage > 0) { + filterOptions(currentPage - 1, lastFilter); + } + break; + case KeyboardListener.KEY_ENTER: + case KeyboardListener.KEY_TAB: + suggestionPopup.menu.doSelectedItemAction(); + break; + } + } + } + + public void onKeyPress(Widget sender, char keyCode, int modifiers) { + + } + + public void onKeyUp(Widget sender, char keyCode, int modifiers) { + if (enabled) { + switch (keyCode) { + case KeyboardListener.KEY_ENTER: + case KeyboardListener.KEY_TAB: + case KeyboardListener.KEY_SHIFT: + case KeyboardListener.KEY_CTRL: + case KeyboardListener.KEY_ALT: + ; // NOP + break; + case KeyboardListener.KEY_DOWN: + case KeyboardListener.KEY_UP: + case KeyboardListener.KEY_PAGEDOWN: + case KeyboardListener.KEY_PAGEUP: + if (suggestionPopup.isAttached()) { + break; + } else { + // open popup as from gadget + filterOptions(-1, ""); + lastFilter = ""; + tb.selectAll(); + break; + } + case KeyboardListener.KEY_ESCAPE: + if (currentSuggestion != null) { + String text = currentSuggestion.getReplacementString(); + setPromptingOff(text); + selectedOptionKey = currentSuggestion.key; + } else { + setPromptingOn(); + selectedOptionKey = null; + } + lastFilter = ""; + suggestionPopup.hide(); + break; + default: + filterOptions(currentPage); + break; + } + } + } + + /** + * Listener for popupopener + */ + public void onClick(Widget sender) { + if (enabled) { + // ask suggestionPopup if it was just closed, we are using GWT + // Popup's auto close feature + if (!suggestionPopup.isJustClosed()) { + filterOptions(-1, ""); + popupOpenerClicked = true; + lastFilter = ""; + } else if (selectedOptionKey == null) { + tb.setText(inputPrompt); + prompting = true; + } + DOM.eventPreventDefault(DOM.eventGetCurrentEvent()); + tb.setFocus(true); + tb.selectAll(); + + } + } + + /* + * Calculate minumum width for FilterSelect textarea + */ + private native int minWidth(String captions) + /*-{ + if(!captions || captions.length <= 0) + return 0; + captions = captions.split("|"); + var d = $wnd.document.createElement("div"); + var html = ""; + for(var i=0; i < captions.length; i++) { + html += "
" + captions[i] + "
"; + // TODO apply same CSS classname as in suggestionmenu + } + d.style.position = "absolute"; + d.style.top = "0"; + d.style.left = "0"; + d.style.visibility = "hidden"; + d.innerHTML = html; + $wnd.document.body.appendChild(d); + var w = d.offsetWidth; + $wnd.document.body.removeChild(d); + return w; + }-*/; + + public void onFocus(Widget sender) { + focused = true; + if (prompting) { + setPromptingOff(""); + } + addStyleDependentName("focus"); + } + + public void onLostFocus(Widget sender) { + focused = false; + if (!suggestionPopup.isAttached() || suggestionPopup.isJustClosed()) { + // typing so fast the popup was never opened, or it's just closed + suggestionPopup.menu.doSelectedItemAction(); + } + if (selectedOptionKey == null) { + setPromptingOn(); + } + removeStyleDependentName("focus"); + } + + public void focus() { + focused = true; + if (prompting) { + setPromptingOff(""); + } + tb.setFocus(true); + } + + @Override + public void setWidth(String width) { + if (width == null || width.equals("")) { + this.width = null; + } else { + this.width = width; + } + Util.setWidthExcludingPaddingAndBorder(this, width, 4); + updateRootWidth(); + } + + @Override + public void setHeight(String height) { + super.setHeight(height); + Util.setHeightExcludingPaddingAndBorder(tb, height, 3); + } + + private void updateRootWidth() { + if (width == null) { + /* + * When the width is not specified we must specify width for root + * div so the popupopener won't wrap to the next line and also so + * the size of the combobox won't change over time. + */ + int tbWidth = Util.getRequiredWidth(tb); + int openerWidth = Util.getRequiredWidth(popupOpener); + int iconWidth = Util.getRequiredWidth(selectedItemIcon); + + int w = tbWidth + openerWidth + iconWidth; + if (suggestionPopupMinWidth > w) { + setTextboxWidth(suggestionPopupMinWidth); + w = suggestionPopupMinWidth; + } else { + /* + * Firefox3 has its own way of doing rendering so we need to + * specify the width for the TextField to make sure it actually + * is rendered as wide as FF3 says it is + */ + tb.setWidth((tbWidth - getTextboxPadding()) + "px"); + } + super.setWidth((w) + "px"); + // Freeze the initial width, so that it won't change even if the + // icon size changes + width = w + "px"; + + } else { + /* + * When the width is specified we also want to explicitly specify + * widths for textbox and popupopener + */ + setTextboxWidth(getMainWidth() - getComponentPadding()); + + } + } + + private int getMainWidth() { + int componentWidth; + if (BrowserInfo.get().isIE6()) { + // Required in IE when textfield is wider than this.width + DOM.setStyleAttribute(getElement(), "overflow", "hidden"); + componentWidth = getOffsetWidth(); + DOM.setStyleAttribute(getElement(), "overflow", ""); + } else { + componentWidth = getOffsetWidth(); + } + return componentWidth; + } + + private void setTextboxWidth(int componentWidth) { + int padding = getTextboxPadding(); + int popupOpenerWidth = Util.getRequiredWidth(popupOpener); + int iconWidth = Util.getRequiredWidth(selectedItemIcon); + int textboxWidth = componentWidth - padding - popupOpenerWidth + - iconWidth; + if (textboxWidth < 0) { + textboxWidth = 0; + } + tb.setWidth(textboxWidth + "px"); + } + + private int getTextboxPadding() { + if (textboxPadding < 0) { + textboxPadding = Util.measureHorizontalPaddingAndBorder(tb + .getElement(), 4); + } + return textboxPadding; + } + + private int getComponentPadding() { + if (componentPadding < 0) { + componentPadding = Util.measureHorizontalPaddingAndBorder( + getElement(), 3); + } + return componentPadding; + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VForm.java b/src/com/vaadin/terminal/gwt/client/ui/VForm.java new file mode 100644 index 0000000000..05e235851a --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VForm.java @@ -0,0 +1,288 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.Set; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.ComplexPanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.Container; +import com.vaadin.terminal.gwt.client.VErrorMessage; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.RenderInformation; +import com.vaadin.terminal.gwt.client.RenderSpace; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +public class VForm extends ComplexPanel implements Container { + + private String height = ""; + + private String width = ""; + + public static final String CLASSNAME = "i-form"; + + private Container lo; + private Element legend = DOM.createLegend(); + private Element caption = DOM.createSpan(); + private Element errorIndicatorElement = DOM.createDiv(); + private Element desc = DOM.createDiv(); + private Icon icon; + private VErrorMessage errorMessage = new VErrorMessage(); + + private Element fieldContainer = DOM.createDiv(); + + private Element footerContainer = DOM.createDiv(); + + private Element fieldSet = DOM.createFieldSet(); + + private Container footer; + + private ApplicationConnection client; + + private RenderInformation renderInformation = new RenderInformation(); + + private int borderPaddingHorizontal; + + private int borderPaddingVertical; + + private boolean rendering = false; + + public VForm() { + setElement(DOM.createDiv()); + DOM.appendChild(getElement(), fieldSet); + setStyleName(CLASSNAME); + DOM.appendChild(fieldSet, legend); + DOM.appendChild(legend, caption); + DOM.setElementProperty(errorIndicatorElement, "className", + "i-errorindicator"); + DOM.setStyleAttribute(errorIndicatorElement, "display", "none"); + DOM.setInnerText(errorIndicatorElement, " "); // needed for IE + DOM.setElementProperty(desc, "className", "i-form-description"); + DOM.appendChild(fieldSet, desc); + DOM.appendChild(fieldSet, fieldContainer); + errorMessage.setVisible(false); + errorMessage.setStyleName(CLASSNAME + "-errormessage"); + DOM.appendChild(fieldSet, errorMessage.getElement()); + DOM.appendChild(fieldSet, footerContainer); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + rendering = true; + boolean measure = false; + if (this.client == null) { + this.client = client; + measure = true; + } + + if (client.updateComponent(this, uidl, false)) { + rendering = false; + return; + } + + if (measure) { + // Measure the border when the style names have been set + borderPaddingVertical = getOffsetHeight(); + int ow = getOffsetWidth(); + int dow = desc.getOffsetWidth(); + borderPaddingHorizontal = ow - dow; + } + + boolean legendEmpty = true; + if (uidl.hasAttribute("caption")) { + DOM.setInnerText(caption, uidl.getStringAttribute("caption")); + legendEmpty = false; + } else { + DOM.setInnerText(caption, ""); + } + if (uidl.hasAttribute("icon")) { + if (icon == null) { + icon = new Icon(client); + DOM.insertChild(legend, icon.getElement(), 0); + } + icon.setUri(uidl.getStringAttribute("icon")); + legendEmpty = false; + } else { + if (icon != null) { + DOM.removeChild(legend, icon.getElement()); + } + } + if (legendEmpty) { + DOM.setStyleAttribute(legend, "display", "none"); + } else { + DOM.setStyleAttribute(legend, "display", ""); + } + + if (uidl.hasAttribute("error")) { + final UIDL errorUidl = uidl.getErrors(); + errorMessage.updateFromUIDL(errorUidl); + errorMessage.setVisible(true); + + } else { + errorMessage.setVisible(false); + } + + if (uidl.hasAttribute("description")) { + DOM.setInnerHTML(desc, uidl.getStringAttribute("description")); + } else { + DOM.setInnerHTML(desc, ""); + } + + updateSize(); + // TODO Check if this is needed + client.runDescendentsLayout(this); + + final UIDL layoutUidl = uidl.getChildUIDL(0); + Container newLo = (Container) client.getPaintable(layoutUidl); + if (lo == null) { + lo = newLo; + add((Widget) lo, fieldContainer); + } else if (lo != newLo) { + client.unregisterPaintable(lo); + remove((Widget) lo); + lo = newLo; + add((Widget) lo, fieldContainer); + } + lo.updateFromUIDL(layoutUidl, client); + + if (uidl.getChildCount() > 1) { + // render footer + Container newFooter = (Container) client.getPaintable(uidl + .getChildUIDL(1)); + if (footer == null) { + add((Widget) newFooter, footerContainer); + footer = newFooter; + } else if (newFooter != footer) { + remove((Widget) footer); + client.unregisterPaintable(footer); + add((Widget) newFooter, footerContainer); + } + footer = newFooter; + footer.updateFromUIDL(uidl.getChildUIDL(1), client); + } else { + if (footer != null) { + remove((Widget) footer); + client.unregisterPaintable(footer); + } + } + + rendering = false; + } + + public void updateSize() { + + renderInformation.updateSize(getElement()); + + renderInformation.setContentAreaHeight(renderInformation + .getRenderedSize().getHeight() + - borderPaddingVertical); + if (BrowserInfo.get().isIE6()) { + getElement().getStyle().setProperty("overflow", "hidden"); + } + renderInformation.setContentAreaWidth(renderInformation + .getRenderedSize().getWidth() + - borderPaddingHorizontal); + } + + public RenderSpace getAllocatedSpace(Widget child) { + if (child == lo) { + int hPixels = 0; + if (!"".equals(height)) { + hPixels = getOffsetHeight(); + hPixels -= borderPaddingVertical; + hPixels -= footerContainer.getOffsetHeight(); + hPixels -= errorMessage.getOffsetHeight(); + hPixels -= desc.getOffsetHeight(); + + } + + return new RenderSpace(renderInformation.getContentAreaSize() + .getWidth(), hPixels); + } else if (child == footer) { + return new RenderSpace(footerContainer.getOffsetWidth(), 0); + } else { + ApplicationConnection.getConsole().error( + "Invalid child requested RenderSpace information"); + return null; + } + } + + public boolean hasChildComponent(Widget component) { + return component != null && (component == lo || component == footer); + } + + public void replaceChildComponent(Widget oldComponent, Widget newComponent) { + if (!hasChildComponent(oldComponent)) { + throw new IllegalArgumentException( + "Old component is not inside this Container"); + } + remove(oldComponent); + if (oldComponent == lo) { + lo = (Container) newComponent; + add((Widget) lo, fieldContainer); + } else { + footer = (Container) newComponent; + add((Widget) footer, footerContainer); + } + + } + + public boolean requestLayout(Set child) { + + if (height != null && width != null) { + /* + * If the height and width has been specified the child components + * cannot make the size of the layout change + */ + + return true; + } + + if (renderInformation.updateSize(getElement())) { + return false; + } else { + return true; + } + + } + + public void updateCaption(Paintable component, UIDL uidl) { + // NOP form don't render caption for neither field layout nor footer + // layout + } + + @Override + public void setHeight(String height) { + if (this.height.equals(height)) { + return; + } + + this.height = height; + super.setHeight(height); + + updateSize(); + } + + @Override + public void setWidth(String width) { + if (Util.equals(this.width, width)) { + return; + } + + this.width = width; + super.setWidth(width); + + updateSize(); + + if (!rendering && height.equals("")) { + // Width might affect height + Util.updateRelativeChildrenAndSendSizeUpdateEvent(client, this); + } + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VFormLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VFormLayout.java new file mode 100644 index 0000000000..4ffc67b4fb --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VFormLayout.java @@ -0,0 +1,464 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Set; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.FlexTable; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.Container; +import com.vaadin.terminal.gwt.client.VTooltip; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.RenderSpace; +import com.vaadin.terminal.gwt.client.StyleConstants; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +/** + * Two col Layout that places caption on left col and field on right col + */ +public class VFormLayout extends SimplePanel implements Container { + + private final static String CLASSNAME = "i-formlayout"; + + private ApplicationConnection client; + private VFormLayoutTable table; + + private String width = ""; + private String height = ""; + + private boolean rendering = false; + + public VFormLayout() { + super(); + setStylePrimaryName(CLASSNAME); + table = new VFormLayoutTable(); + setWidget(table); + } + + public class VFormLayoutTable extends FlexTable { + + private static final int COLUMN_CAPTION = 0; + private static final int COLUMN_ERRORFLAG = 1; + private static final int COLUMN_WIDGET = 2; + + private HashMap componentToCaption = new HashMap(); + private HashMap componentToError = new HashMap(); + + public VFormLayoutTable() { + DOM.setElementProperty(getElement(), "cellPadding", "0"); + DOM.setElementProperty(getElement(), "cellSpacing", "0"); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + final VMarginInfo margins = new VMarginInfo(uidl + .getIntAttribute("margins")); + + Element margin = getElement(); + setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_TOP, + margins.hasTop()); + setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_RIGHT, + margins.hasRight()); + setStyleName(margin, + CLASSNAME + "-" + StyleConstants.MARGIN_BOTTOM, margins + .hasBottom()); + setStyleName(margin, CLASSNAME + "-" + StyleConstants.MARGIN_LEFT, + margins.hasLeft()); + + setStyleName(margin, CLASSNAME + "-" + "spacing", uidl + .hasAttribute("spacing")); + + int i = 0; + for (final Iterator it = uidl.getChildIterator(); it.hasNext(); i++) { + prepareCell(i, 1); + final UIDL childUidl = (UIDL) it.next(); + final Paintable p = client.getPaintable(childUidl); + Caption caption = componentToCaption.get(p); + if (caption == null) { + caption = new Caption(p, client); + componentToCaption.put(p, caption); + } + ErrorFlag error = componentToError.get(p); + if (error == null) { + error = new ErrorFlag(); + componentToError.put(p, error); + } + prepareCell(i, COLUMN_WIDGET); + final Paintable oldComponent = (Paintable) getWidget(i, + COLUMN_WIDGET); + if (oldComponent == null) { + setWidget(i, COLUMN_WIDGET, (Widget) p); + } else if (oldComponent != p) { + client.unregisterPaintable(oldComponent); + setWidget(i, COLUMN_WIDGET, (Widget) p); + } + getCellFormatter().setStyleName(i, COLUMN_WIDGET, + CLASSNAME + "-contentcell"); + getCellFormatter().setStyleName(i, COLUMN_CAPTION, + CLASSNAME + "-captioncell"); + setWidget(i, COLUMN_CAPTION, caption); + + setContentWidth(i); + + getCellFormatter().setStyleName(i, COLUMN_ERRORFLAG, + CLASSNAME + "-errorcell"); + setWidget(i, COLUMN_ERRORFLAG, error); + + p.updateFromUIDL(childUidl, client); + + String rowstyles = CLASSNAME + "-row"; + if (i == 0) { + rowstyles += " " + CLASSNAME + "-firstrow"; + } + if (!it.hasNext()) { + rowstyles += " " + CLASSNAME + "-lastrow"; + } + + getRowFormatter().setStyleName(i, rowstyles); + + } + + while (getRowCount() > i) { + final Paintable p = (Paintable) getWidget(i, COLUMN_WIDGET); + client.unregisterPaintable(p); + componentToCaption.remove(p); + removeRow(i); + } + + /* + * Must update relative sized fields last when it is clear how much + * space they are allowed to use + */ + for (Paintable p : componentToCaption.keySet()) { + client.handleComponentRelativeSize((Widget) p); + } + } + + public void setContentWidths() { + for (int row = 0; row < getRowCount(); row++) { + setContentWidth(row); + } + } + + private void setContentWidth(int row) { + String width = ""; + if (!isDynamicWidth()) { + width = "100%"; + } + getCellFormatter().setWidth(row, COLUMN_WIDGET, width); + } + + public void replaceChildComponent(Widget oldComponent, + Widget newComponent) { + int i; + for (i = 0; i < getRowCount(); i++) { + Widget candidate = getWidget(i, COLUMN_WIDGET); + if (oldComponent == candidate) { + final Caption newCap = new Caption( + (Paintable) newComponent, client); + componentToCaption.put((Paintable) newComponent, newCap); + ErrorFlag error = componentToError.get(newComponent); + if (error == null) { + error = new ErrorFlag(); + componentToError.put((Paintable) newComponent, error); + } + + setWidget(i, COLUMN_CAPTION, newCap); + setWidget(i, COLUMN_ERRORFLAG, error); + setWidget(i, COLUMN_WIDGET, newComponent); + break; + } + } + + } + + public boolean hasChildComponent(Widget component) { + return componentToCaption.containsKey(component); + } + + public void updateCaption(Paintable component, UIDL uidl) { + final Caption c = componentToCaption.get(component); + if (c != null) { + c.updateCaption(uidl); + } + final ErrorFlag e = componentToError.get(component); + if (e != null) { + e.updateFromUIDL(uidl, component); + } + + } + + public int getAllocatedWidth(Widget child, int availableWidth) { + Caption caption = componentToCaption.get(child); + ErrorFlag error = componentToError.get(child); + int width = availableWidth; + + if (caption != null) { + width -= DOM.getParent(caption.getElement()).getOffsetWidth(); + } + if (error != null) { + width -= DOM.getParent(error.getElement()).getOffsetWidth(); + } + + return width; + } + + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + rendering = true; + + this.client = client; + + if (client.updateComponent(this, uidl, true)) { + rendering = false; + return; + } + + table.updateFromUIDL(uidl, client); + + rendering = false; + } + + public boolean isDynamicWidth() { + return width.equals(""); + } + + public boolean hasChildComponent(Widget component) { + return table.hasChildComponent(component); + } + + public void replaceChildComponent(Widget oldComponent, Widget newComponent) { + table.replaceChildComponent(oldComponent, newComponent); + } + + public void updateCaption(Paintable component, UIDL uidl) { + table.updateCaption(component, uidl); + } + + public class Caption extends HTML { + + public static final String CLASSNAME = "i-caption"; + + private final Paintable owner; + + private Element requiredFieldIndicator; + + private Icon icon; + + private Element captionText; + + private final ApplicationConnection client; + + /** + * + * @param component + * optional owner of caption. If not set, getOwner will + * return null + * @param client + */ + public Caption(Paintable component, ApplicationConnection client) { + super(); + this.client = client; + owner = component; + setStyleName(CLASSNAME); + sinkEvents(VTooltip.TOOLTIP_EVENTS); + } + + public void updateCaption(UIDL uidl) { + setVisible(!uidl.getBooleanAttribute("invisible")); + + setStyleName(getElement(), "i-disabled", uidl + .hasAttribute("disabled")); + + boolean isEmpty = true; + + if (uidl.hasAttribute("icon")) { + if (icon == null) { + icon = new Icon(client); + + DOM.insertChild(getElement(), icon.getElement(), 0); + } + icon.setUri(uidl.getStringAttribute("icon")); + isEmpty = false; + } else { + if (icon != null) { + DOM.removeChild(getElement(), icon.getElement()); + icon = null; + } + + } + + if (uidl.hasAttribute("caption")) { + if (captionText == null) { + captionText = DOM.createSpan(); + DOM.insertChild(getElement(), captionText, icon == null ? 0 + : 1); + } + String c = uidl.getStringAttribute("caption"); + if (c == null) { + c = ""; + } else { + isEmpty = false; + } + DOM.setInnerText(captionText, c); + } else { + // TODO should span also be removed + } + + if (uidl.hasAttribute("description")) { + if (captionText != null) { + addStyleDependentName("hasdescription"); + } else { + removeStyleDependentName("hasdescription"); + } + } + + if (uidl.getBooleanAttribute("required")) { + if (requiredFieldIndicator == null) { + requiredFieldIndicator = DOM.createSpan(); + DOM.setInnerText(requiredFieldIndicator, "*"); + DOM.setElementProperty(requiredFieldIndicator, "className", + "i-required-field-indicator"); + DOM.appendChild(getElement(), requiredFieldIndicator); + } + } else { + if (requiredFieldIndicator != null) { + DOM.removeChild(getElement(), requiredFieldIndicator); + requiredFieldIndicator = null; + } + } + + // Workaround for IE weirdness, sometimes returns bad height in some + // circumstances when Caption is empty. See #1444 + // IE7 bugs more often. I wonder what happens when IE8 arrives... + if (Util.isIE()) { + if (isEmpty) { + setHeight("0px"); + DOM.setStyleAttribute(getElement(), "overflow", "hidden"); + } else { + setHeight(""); + DOM.setStyleAttribute(getElement(), "overflow", ""); + } + + } + + } + + /** + * Returns Paintable for which this Caption belongs to. + * + * @return owner Widget + */ + public Paintable getOwner() { + return owner; + } + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + if (client != null) { + client.handleTooltipEvent(event, owner); + } + } + } + + private class ErrorFlag extends HTML { + private static final String CLASSNAME = VFormLayout.CLASSNAME + + "-error-indicator"; + Element errorIndicatorElement; + private Paintable owner; + + public ErrorFlag() { + setStyleName(CLASSNAME); + sinkEvents(VTooltip.TOOLTIP_EVENTS); + } + + public void updateFromUIDL(UIDL uidl, Paintable component) { + owner = component; + if (uidl.hasAttribute("error") + && !uidl.getBooleanAttribute("hideErrors")) { + if (errorIndicatorElement == null) { + errorIndicatorElement = DOM.createDiv(); + DOM.setInnerHTML(errorIndicatorElement, " "); + DOM.setElementProperty(errorIndicatorElement, "className", + "i-errorindicator"); + DOM.appendChild(getElement(), errorIndicatorElement); + } + + } else if (errorIndicatorElement != null) { + DOM.removeChild(getElement(), errorIndicatorElement); + errorIndicatorElement = null; + } + } + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + if (owner != null) { + client.handleTooltipEvent(event, owner); + } + } + + } + + public boolean requestLayout(Set child) { + if (height.equals("") || width.equals("")) { + // A dynamic size might change due to children changes + return false; + } + + return true; + } + + public RenderSpace getAllocatedSpace(Widget child) { + int width = 0; + int height = 0; + + if (!this.width.equals("")) { + int availableWidth = getOffsetWidth(); + width = table.getAllocatedWidth(child, availableWidth); + } + + return new RenderSpace(width, height, false); + } + + @Override + public void setHeight(String height) { + if (this.height.equals(height)) { + return; + } + + this.height = height; + super.setHeight(height); + } + + @Override + public void setWidth(String width) { + if (this.width.equals(width)) { + return; + } + + this.width = width; + super.setWidth(width); + + if (!rendering) { + table.setContentWidths(); + if (height.equals("")) { + // Width might affect height + Util.updateRelativeChildrenAndSendSizeUpdateEvent(client, this); + } + } + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VGridLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VGridLayout.java new file mode 100644 index 0000000000..ebaad51785 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VGridLayout.java @@ -0,0 +1,1018 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.Document; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.AbsolutePanel; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.Container; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.RenderSpace; +import com.vaadin.terminal.gwt.client.StyleConstants; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; +import com.vaadin.terminal.gwt.client.ui.layout.CellBasedLayout; +import com.vaadin.terminal.gwt.client.ui.layout.ChildComponentContainer; + +public class VGridLayout extends SimplePanel implements Paintable, Container { + + public static final String CLASSNAME = "i-gridlayout"; + + private DivElement margin = Document.get().createDivElement(); + + private final AbsolutePanel canvas = new AbsolutePanel(); + + private ApplicationConnection client; + + protected HashMap widgetToComponentContainer = new HashMap(); + + private HashMap paintableToCell = new HashMap(); + + private int spacingPixelsHorizontal; + private int spacingPixelsVertical; + + private int[] columnWidths; + private int[] rowHeights; + + private String height; + + private String width; + + private int[] colExpandRatioArray; + + private int[] rowExpandRatioArray; + + private int[] minColumnWidths; + + private int[] minRowHeights; + + private boolean rendering; + + private HashMap nonRenderedWidgets; + + private boolean sizeChangedDuringRendering = false; + + public VGridLayout() { + super(); + getElement().appendChild(margin); + setStyleName(CLASSNAME); + setWidget(canvas); + } + + @Override + protected Element getContainerElement() { + return margin.cast(); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + rendering = true; + this.client = client; + + if (client.updateComponent(this, uidl, true)) { + rendering = false; + return; + } + + boolean mightToggleVScrollBar = "".equals(height) && !"".equals(width); + boolean mightToggleHScrollBar = "".equals(width) && !"".equals(height); + int wBeforeRender = 0; + int hBeforeRender = 0; + if (mightToggleHScrollBar || mightToggleVScrollBar) { + wBeforeRender = canvas.getOffsetWidth(); + hBeforeRender = getOffsetHeight(); + } + canvas.setWidth("0px"); + + handleMargins(uidl); + detectSpacing(uidl); + + int cols = uidl.getIntAttribute("w"); + int rows = uidl.getIntAttribute("h"); + + columnWidths = new int[cols]; + rowHeights = new int[rows]; + + if (cells == null) { + cells = new Cell[cols][rows]; + } else if (cells.length != cols || cells[0].length != rows) { + Cell[][] newCells = new Cell[cols][rows]; + for (int i = 0; i < cells.length; i++) { + for (int j = 0; j < cells[i].length; j++) { + if (i < cols && j < rows) { + newCells[i][j] = cells[i][j]; + } + } + } + cells = newCells; + } + + nonRenderedWidgets = (HashMap) widgetToComponentContainer + .clone(); + + final int[] alignments = uidl.getIntArrayAttribute("alignments"); + int alignmentIndex = 0; + + LinkedList pendingCells = new LinkedList(); + + LinkedList relativeHeighted = new LinkedList(); + + for (final Iterator i = uidl.getChildIterator(); i.hasNext();) { + final UIDL r = (UIDL) i.next(); + if ("gr".equals(r.getTag())) { + for (final Iterator j = r.getChildIterator(); j.hasNext();) { + final UIDL c = (UIDL) j.next(); + if ("gc".equals(c.getTag())) { + Cell cell = getCell(c); + if (cell.hasContent()) { + boolean rendered = cell.renderIfNoRelativeWidth(); + cell.alignment = alignments[alignmentIndex++]; + if (!rendered) { + pendingCells.add(cell); + } + + if (cell.colspan > 1) { + storeColSpannedCell(cell); + } else if (rendered) { + // strore non-colspanned widths to columnWidth + // array + if (columnWidths[cell.col] < cell.getWidth()) { + columnWidths[cell.col] = cell.getWidth(); + } + } + if (cell.hasRelativeHeight()) { + relativeHeighted.add(cell); + } + } + } + } + } + } + + distributeColSpanWidths(); + colExpandRatioArray = uidl.getIntArrayAttribute("colExpand"); + rowExpandRatioArray = uidl.getIntArrayAttribute("rowExpand"); + + minColumnWidths = cloneArray(columnWidths); + expandColumns(); + + renderRemainingComponentsWithNoRelativeHeight(pendingCells); + + detectRowHeights(); + + expandRows(); + + renderRemainingComponents(pendingCells); + + for (Cell cell : relativeHeighted) { + Widget widget2 = cell.cc.getWidget(); + client.handleComponentRelativeSize(widget2); + cell.cc.updateWidgetSize(); + } + + layoutCells(); + + // clean non rendered components + for (Widget w : nonRenderedWidgets.keySet()) { + ChildComponentContainer childComponentContainer = widgetToComponentContainer + .get(w); + paintableToCell.remove(w); + widgetToComponentContainer.remove(w); + childComponentContainer.removeFromParent(); + client.unregisterPaintable((Paintable) w); + } + nonRenderedWidgets = null; + + rendering = false; + sizeChangedDuringRendering = false; + + boolean needsRelativeSizeCheck = false; + + if (mightToggleHScrollBar && wBeforeRender != canvas.getOffsetWidth()) { + needsRelativeSizeCheck = true; + } + if (mightToggleVScrollBar && hBeforeRender != getOffsetHeight()) { + needsRelativeSizeCheck = true; + } + if (needsRelativeSizeCheck) { + client.handleComponentRelativeSize(this); + } + } + + private static int[] cloneArray(int[] toBeCloned) { + int[] clone = new int[toBeCloned.length]; + for (int i = 0; i < clone.length; i++) { + clone[i] = toBeCloned[i] * 1; + } + return clone; + } + + private void expandRows() { + if (!"".equals(height)) { + int usedSpace = minRowHeights[0]; + for (int i = 1; i < minRowHeights.length; i++) { + usedSpace += spacingPixelsVertical + minRowHeights[i]; + } + int availableSpace = getOffsetHeight() - marginTopAndBottom; + int excessSpace = availableSpace - usedSpace; + int distributed = 0; + if (excessSpace > 0) { + for (int i = 0; i < rowHeights.length; i++) { + int ew = excessSpace * rowExpandRatioArray[i] / 1000; + rowHeights[i] = minRowHeights[i] + ew; + distributed += ew; + } + excessSpace -= distributed; + int c = 0; + while (excessSpace > 0) { + rowHeights[c % rowHeights.length]++; + excessSpace--; + c++; + } + } + } + } + + @Override + public void setHeight(String height) { + super.setHeight(height); + if (!height.equals(this.height)) { + this.height = height; + if (rendering) { + sizeChangedDuringRendering = true; + } else { + expandRows(); + layoutCells(); + for (Paintable c : paintableToCell.keySet()) { + client.handleComponentRelativeSize((Widget) c); + } + } + } + } + + @Override + public void setWidth(String width) { + super.setWidth(width); + if (!width.equals(this.width)) { + this.width = width; + if (rendering) { + sizeChangedDuringRendering = true; + } else { + int[] oldWidths = cloneArray(columnWidths); + expandColumns(); + boolean heightChanged = false; + HashSet dirtyRows = null; + for (int i = 0; i < oldWidths.length; i++) { + if (columnWidths[i] != oldWidths[i]) { + Cell[] column = cells[i]; + for (int j = 0; j < column.length; j++) { + Cell c = column[j]; + if (c != null && c.cc != null + && c.widthCanAffectHeight()) { + c.cc.setContainerSize(c.getAvailableWidth(), c + .getAvailableHeight()); + client.handleComponentRelativeSize(c.cc + .getWidget()); + c.cc.updateWidgetSize(); + int newHeight = c.getHeight(); + if (columnWidths[i] < oldWidths[i] + && newHeight > minRowHeights[j]) { + minRowHeights[j] = newHeight; + if (newHeight > rowHeights[j]) { + rowHeights[j] = newHeight; + heightChanged = true; + } + } else if (newHeight < minRowHeights[j]) { + // need to recalculate new minimum height + // for this row + if (dirtyRows == null) { + dirtyRows = new HashSet(); + } + dirtyRows.add(j); + } + } + } + } + } + if (dirtyRows != null) { + /* flag indicating that there is a potential row shrinking */ + boolean rowMayShrink = false; + for (Integer rowIndex : dirtyRows) { + int oldMinimum = minRowHeights[rowIndex]; + int newMinimum = 0; + for (int colIndex = 0; colIndex < columnWidths.length; colIndex++) { + Cell cell = cells[colIndex][rowIndex]; + if (cell != null && !cell.hasRelativeHeight() + && cell.getHeight() > newMinimum) { + newMinimum = cell.getHeight(); + } + } + if (newMinimum < oldMinimum) { + minRowHeights[rowIndex] = rowHeights[rowIndex] = newMinimum; + rowMayShrink = true; + } + } + if (rowMayShrink) { + distributeRowSpanHeights(); + minRowHeights = cloneArray(rowHeights); + heightChanged = true; + } + + } + layoutCells(); + for (Paintable c : paintableToCell.keySet()) { + client.handleComponentRelativeSize((Widget) c); + } + if (heightChanged && "".equals(height)) { + Util.notifyParentOfSizeChange(this, false); + } + } + } + } + + private void expandColumns() { + if (!"".equals(width)) { + int usedSpace = minColumnWidths[0]; + for (int i = 1; i < minColumnWidths.length; i++) { + usedSpace += spacingPixelsHorizontal + minColumnWidths[i]; + } + canvas.setWidth(""); + int availableSpace = canvas.getOffsetWidth(); + int excessSpace = availableSpace - usedSpace; + int distributed = 0; + if (excessSpace > 0) { + for (int i = 0; i < columnWidths.length; i++) { + int ew = excessSpace * colExpandRatioArray[i] / 1000; + columnWidths[i] = minColumnWidths[i] + ew; + distributed += ew; + } + excessSpace -= distributed; + int c = 0; + while (excessSpace > 0) { + columnWidths[c % columnWidths.length]++; + excessSpace--; + c++; + } + } + } + } + + private void layoutCells() { + int x = 0; + int y = 0; + for (int i = 0; i < cells.length; i++) { + y = 0; + for (int j = 0; j < cells[i].length; j++) { + Cell cell = cells[i][j]; + if (cell != null) { + cell.layout(x, y); + } + y += rowHeights[j] + spacingPixelsVertical; + } + x += columnWidths[i] + spacingPixelsHorizontal; + } + + if ("".equals(width)) { + canvas.setWidth((x - spacingPixelsHorizontal) + "px"); + } else { + // main element defines width + canvas.setWidth(""); + } + int canvasHeight; + if ("".equals(height)) { + canvasHeight = y - spacingPixelsVertical; + } else { + canvasHeight = getOffsetHeight() - marginTopAndBottom; + } + canvas.setHeight(canvasHeight + "px"); + } + + private void renderRemainingComponents(LinkedList pendingCells) { + for (Cell cell : pendingCells) { + cell.render(); + } + } + + private void detectRowHeights() { + + // collect min rowheight from non-rowspanned cells + for (int i = 0; i < cells.length; i++) { + for (int j = 0; j < cells[i].length; j++) { + Cell cell = cells[i][j]; + if (cell != null) { + /* + * Setting fixing container width may in some situations + * affect height. Example: Label with wrapping text without + * or with relative width. + */ + if (cell.cc != null && cell.widthCanAffectHeight()) { + cell.cc.setWidth(cell.getAvailableWidth() + "px"); + cell.cc.updateWidgetSize(); + } + if (cell.rowspan == 1) { + if (!cell.hasRelativeHeight() + && rowHeights[j] < cell.getHeight()) { + rowHeights[j] = cell.getHeight(); + } + } else { + storeRowSpannedCell(cell); + } + } + } + } + + distributeRowSpanHeights(); + + minRowHeights = cloneArray(rowHeights); + } + + private void storeRowSpannedCell(Cell cell) { + SpanList l = null; + for (SpanList list : rowSpans) { + if (list.span < cell.rowspan) { + continue; + } else { + // insert before this + l = list; + break; + } + } + if (l == null) { + l = new SpanList(cell.rowspan); + rowSpans.add(l); + } else if (l.span != cell.rowspan) { + SpanList newL = new SpanList(cell.rowspan); + rowSpans.add(rowSpans.indexOf(l), newL); + l = newL; + } + l.cells.add(cell); + } + + private void renderRemainingComponentsWithNoRelativeHeight( + LinkedList pendingCells) { + + for (Iterator iterator = pendingCells.iterator(); iterator.hasNext();) { + Cell cell = (Cell) iterator.next(); + if (!cell.hasRelativeHeight()) { + cell.render(); + iterator.remove(); + } + } + + } + + /** + * Iterates colspanned cells, ensures cols have enough space to accommodate + * them + */ + private void distributeColSpanWidths() { + for (SpanList list : colSpans) { + for (Cell cell : list.cells) { + int width = cell.getWidth(); + int allocated = columnWidths[cell.col]; + for (int i = 1; i < cell.colspan; i++) { + allocated += spacingPixelsHorizontal + + columnWidths[cell.col + i]; + } + if (allocated < width) { + // columnWidths needs to be expanded due colspanned cell + int neededExtraSpace = width - allocated; + int spaceForColunms = neededExtraSpace / cell.colspan; + for (int i = 0; i < cell.colspan; i++) { + int col = cell.col + i; + columnWidths[col] += spaceForColunms; + neededExtraSpace -= spaceForColunms; + } + if (neededExtraSpace > 0) { + for (int i = 0; i < cell.colspan; i++) { + int col = cell.col + i; + columnWidths[col] += 1; + neededExtraSpace -= 1; + if (neededExtraSpace == 0) { + break; + } + } + } + } + } + } + } + + /** + * Iterates rowspanned cells, ensures rows have enough space to accommodate + * them + */ + private void distributeRowSpanHeights() { + for (SpanList list : rowSpans) { + for (Cell cell : list.cells) { + int height = cell.getHeight(); + int allocated = rowHeights[cell.row]; + for (int i = 1; i < cell.rowspan; i++) { + allocated += spacingPixelsVertical + + rowHeights[cell.row + i]; + } + if (allocated < height) { + // columnWidths needs to be expanded due colspanned cell + int neededExtraSpace = height - allocated; + int spaceForColunms = neededExtraSpace / cell.rowspan; + for (int i = 0; i < cell.rowspan; i++) { + int row = cell.row + i; + rowHeights[row] += spaceForColunms; + neededExtraSpace -= spaceForColunms; + } + if (neededExtraSpace > 0) { + for (int i = 0; i < cell.rowspan; i++) { + int row = cell.row + i; + rowHeights[row] += 1; + neededExtraSpace -= 1; + if (neededExtraSpace == 0) { + break; + } + } + } + } + } + } + } + + private LinkedList colSpans = new LinkedList(); + private LinkedList rowSpans = new LinkedList(); + + private int marginTopAndBottom; + + private class SpanList { + final int span; + List cells = new LinkedList(); + + public SpanList(int span) { + this.span = span; + } + } + + private void storeColSpannedCell(Cell cell) { + SpanList l = null; + for (SpanList list : colSpans) { + if (list.span < cell.colspan) { + continue; + } else { + // insert before this + l = list; + break; + } + } + if (l == null) { + l = new SpanList(cell.colspan); + colSpans.add(l); + } else if (l.span != cell.colspan) { + + SpanList newL = new SpanList(cell.colspan); + colSpans.add(colSpans.indexOf(l), newL); + l = newL; + } + l.cells.add(cell); + } + + private void detectSpacing(UIDL uidl) { + DivElement spacingmeter = Document.get().createDivElement(); + spacingmeter.setClassName(CLASSNAME + "-" + "spacing-" + + (uidl.getBooleanAttribute("spacing") ? "on" : "off")); + spacingmeter.getStyle().setProperty("width", "0"); + spacingmeter.getStyle().setProperty("height", "0"); + canvas.getElement().appendChild(spacingmeter); + spacingPixelsHorizontal = spacingmeter.getOffsetWidth(); + spacingPixelsVertical = spacingmeter.getOffsetHeight(); + canvas.getElement().removeChild(spacingmeter); + } + + private void handleMargins(UIDL uidl) { + final VMarginInfo margins = new VMarginInfo(uidl + .getIntAttribute("margins")); + + String styles = CLASSNAME + "-margin"; + if (margins.hasTop()) { + styles += " " + CLASSNAME + "-" + StyleConstants.MARGIN_TOP; + } + if (margins.hasRight()) { + styles += " " + CLASSNAME + "-" + StyleConstants.MARGIN_RIGHT; + } + if (margins.hasBottom()) { + styles += " " + CLASSNAME + "-" + StyleConstants.MARGIN_BOTTOM; + } + if (margins.hasLeft()) { + styles += " " + CLASSNAME + "-" + StyleConstants.MARGIN_LEFT; + } + margin.setClassName(styles); + + marginTopAndBottom = margin.getOffsetHeight() + - canvas.getOffsetHeight(); + } + + public boolean hasChildComponent(Widget component) { + return paintableToCell.containsKey(component); + } + + public void replaceChildComponent(Widget oldComponent, Widget newComponent) { + ChildComponentContainer componentContainer = widgetToComponentContainer + .remove(oldComponent); + if (componentContainer == null) { + return; + } + + componentContainer.setWidget(newComponent); + widgetToComponentContainer.put(newComponent, componentContainer); + + paintableToCell.put((Paintable) newComponent, paintableToCell + .get(oldComponent)); + } + + public void updateCaption(Paintable component, UIDL uidl) { + ChildComponentContainer cc = widgetToComponentContainer.get(component); + if (cc != null) { + cc.updateCaption(uidl, client); + } + if (!rendering) { + // ensure rel size details are updated + paintableToCell.get(component).updateRelSizeStatus(uidl); + } + } + + public boolean requestLayout(final Set changedChildren) { + boolean needsLayout = false; + boolean reDistributeColSpanWidths = false; + boolean reDistributeRowSpanHeights = false; + int offsetHeight = canvas.getOffsetHeight(); + int offsetWidth = canvas.getOffsetWidth(); + if ("".equals(width) || "".equals(height)) { + needsLayout = true; + } + ArrayList dirtyColumns = new ArrayList(); + ArrayList dirtyRows = new ArrayList(); + for (Paintable paintable : changedChildren) { + + Cell cell = paintableToCell.get(paintable); + if (!cell.hasRelativeHeight() || !cell.hasRelativeWidth()) { + // cell sizes will only stay still if only relatively + // sized + // components + // check if changed child affects min col widths + cell.cc.setWidth(""); + cell.cc.setHeight(""); + + cell.cc.updateWidgetSize(); + int width = cell.getWidth(); + int allocated = columnWidths[cell.col]; + for (int i = 1; i < cell.colspan; i++) { + allocated += spacingPixelsHorizontal + + columnWidths[cell.col + i]; + } + if (allocated < width) { + needsLayout = true; + if (cell.colspan == 1) { + // do simple column width expansion + columnWidths[cell.col] = minColumnWidths[cell.col] = width; + } else { + // mark that col span expansion is needed + reDistributeColSpanWidths = true; + } + } else if (allocated != width) { + // size is smaller thant allocated, column might + // shrink + dirtyColumns.add(cell.col); + } + + int height = cell.getHeight(); + + allocated = rowHeights[cell.row]; + for (int i = 1; i < cell.rowspan; i++) { + allocated += spacingPixelsVertical + + rowHeights[cell.row + i]; + } + if (allocated < height) { + needsLayout = true; + if (cell.rowspan == 1) { + // do simple row expansion + rowHeights[cell.row] = minRowHeights[cell.row] = height; + } else { + // mark that row span expansion is needed + reDistributeRowSpanHeights = true; + } + } else if (allocated != height) { + // size is smaller than allocated, row might shrink + dirtyRows.add(cell.row); + } + } + } + + if (dirtyColumns.size() > 0) { + for (Integer colIndex : dirtyColumns) { + int colW = 0; + for (int i = 0; i < rowHeights.length; i++) { + Cell cell = cells[colIndex][i]; + if (cell != null && cell.getChildUIDL() != null + && !cell.hasRelativeWidth() && cell.colspan == 1) { + int width = cell.getWidth(); + if (width > colW) { + colW = width; + } + } + } + minColumnWidths[colIndex] = colW; + } + needsLayout = true; + // ensure colspanned columns have enough space + columnWidths = cloneArray(minColumnWidths); + distributeColSpanWidths(); + reDistributeColSpanWidths = false; + } + + if (reDistributeColSpanWidths) { + distributeColSpanWidths(); + } + + if (dirtyRows.size() > 0) { + needsLayout = true; + for (Integer rowIndex : dirtyRows) { + // recalculate min row height + int rowH = minRowHeights[rowIndex] = 0; + // loop all columns on row rowIndex + for (int i = 0; i < columnWidths.length; i++) { + Cell cell = cells[i][rowIndex]; + if (cell != null && cell.getChildUIDL() != null + && !cell.hasRelativeHeight() && cell.rowspan == 1) { + int h = cell.getHeight(); + if (h > rowH) { + rowH = h; + } + } + } + minRowHeights[rowIndex] = rowH; + } + // TODO could check only some row spans + rowHeights = cloneArray(minRowHeights); + distributeRowSpanHeights(); + reDistributeRowSpanHeights = false; + } + + if (reDistributeRowSpanHeights) { + distributeRowSpanHeights(); + } + + if (needsLayout) { + expandColumns(); + expandRows(); + layoutCells(); + // loop all relative sized components and update their size + for (int i = 0; i < cells.length; i++) { + for (int j = 0; j < cells[i].length; j++) { + Cell cell = cells[i][j]; + if (cell != null + && cell.cc != null + && (cell.hasRelativeHeight() || cell + .hasRelativeWidth())) { + client.handleComponentRelativeSize(cell.cc.getWidget()); + } + } + } + } + if (canvas.getOffsetHeight() != offsetHeight + || canvas.getOffsetWidth() != offsetWidth) { + return false; + } else { + return true; + } + } + + public RenderSpace getAllocatedSpace(Widget child) { + Cell cell = paintableToCell.get(child); + assert cell != null; + return cell.getAllocatedSpace(); + } + + private Cell[][] cells; + + /** + * Private helper class. + */ + private class Cell { + private boolean relHeight = false; + private boolean relWidth = false; + private boolean widthCanAffectHeight = false; + + public Cell(UIDL c) { + row = c.getIntAttribute("y"); + col = c.getIntAttribute("x"); + setUidl(c); + } + + public boolean widthCanAffectHeight() { + return widthCanAffectHeight; + } + + public boolean hasRelativeHeight() { + return relHeight; + } + + public RenderSpace getAllocatedSpace() { + return new RenderSpace(getAvailableWidth() + - cc.getCaptionWidthAfterComponent(), getAvailableHeight() + - cc.getCaptionHeightAboveComponent()); + } + + public boolean hasContent() { + return childUidl != null; + } + + /** + * @return total of spanned cols + */ + private int getAvailableWidth() { + int width = columnWidths[col]; + for (int i = 1; i < colspan; i++) { + width += spacingPixelsHorizontal + columnWidths[col + i]; + } + return width; + } + + /** + * @return total of spanned rows + */ + private int getAvailableHeight() { + int height = rowHeights[row]; + for (int i = 1; i < rowspan; i++) { + height += spacingPixelsVertical + rowHeights[row + i]; + } + return height; + } + + public void layout(int x, int y) { + if (cc != null && cc.isAttached()) { + canvas.setWidgetPosition(cc, x, y); + cc.setContainerSize(getAvailableWidth(), getAvailableHeight()); + cc.setAlignment(new AlignmentInfo(alignment)); + cc.updateAlignments(getAvailableWidth(), getAvailableHeight()); + } + } + + public int getWidth() { + if (cc != null) { + int w = cc.getWidgetSize().getWidth() + + cc.getCaptionWidthAfterComponent(); + return w; + } else { + return 0; + } + } + + public int getHeight() { + if (cc != null) { + return cc.getWidgetSize().getHeight() + + cc.getCaptionHeightAboveComponent(); + } else { + return 0; + } + } + + public boolean renderIfNoRelativeWidth() { + if (childUidl == null) { + return false; + } + if (!hasRelativeWidth()) { + render(); + return true; + } else { + return false; + } + } + + protected boolean hasRelativeWidth() { + return relWidth; + } + + protected void render() { + assert childUidl != null; + + Paintable paintable = client.getPaintable(childUidl); + assert paintable != null; + if (cc == null || cc.getWidget() != paintable) { + if (widgetToComponentContainer.containsKey(paintable)) { + cc = widgetToComponentContainer.get(paintable); + cc.setWidth(""); + cc.setHeight(""); + } else { + cc = new ChildComponentContainer((Widget) paintable, + CellBasedLayout.ORIENTATION_VERTICAL); + widgetToComponentContainer.put((Widget) paintable, cc); + paintableToCell.put(paintable, this); + cc.setWidth(""); + canvas.add(cc, 0, 0); + } + } + cc.renderChild(childUidl, client, -1); + if (sizeChangedDuringRendering && Util.isCached(childUidl)) { + client.handleComponentRelativeSize(cc.getWidget()); + } + cc.updateWidgetSize(); + nonRenderedWidgets.remove(paintable); + } + + public UIDL getChildUIDL() { + return childUidl; + } + + final int row; + final int col; + int colspan = 1; + int rowspan = 1; + UIDL childUidl; + int alignment; + ChildComponentContainer cc; + + public void setUidl(UIDL c) { + // Set cell width + colspan = c.hasAttribute("w") ? c.getIntAttribute("w") : 1; + // Set cell height + rowspan = c.hasAttribute("h") ? c.getIntAttribute("h") : 1; + // ensure we will lose reference to old cells, now overlapped by + // this cell + for (int i = 0; i < colspan; i++) { + for (int j = 0; j < rowspan; j++) { + if (i > 0 || j > 0) { + cells[col + i][row + j] = null; + } + } + } + + c = c.getChildUIDL(0); // we are interested about childUidl + if (childUidl != null) { + if (c == null) { + // content has vanished, old content will be removed from + // canvas + // later durin render phase + cc = null; + } else if (cc != null + && cc.getWidget() != client.getPaintable(c)) { + // content has changed + cc = null; + if (widgetToComponentContainer.containsKey(client + .getPaintable(c))) { + // cc exist for this component (moved) use that for this + // cell + cc = widgetToComponentContainer.get(client + .getPaintable(c)); + cc.setWidth(""); + cc.setHeight(""); + } + } + } + childUidl = c; + updateRelSizeStatus(c); + } + + protected void updateRelSizeStatus(UIDL uidl) { + if (uidl != null && !uidl.getBooleanAttribute("cached")) { + if (uidl.hasAttribute("height") + && uidl.getStringAttribute("height").contains("%")) { + relHeight = true; + } else { + relHeight = false; + } + if (uidl.hasAttribute("width")) { + widthCanAffectHeight = relWidth = uidl.getStringAttribute( + "width").contains("%"); + if (uidl.hasAttribute("height")) { + widthCanAffectHeight = false; + } + } else { + widthCanAffectHeight = !uidl.hasAttribute("height"); + relWidth = false; + } + } + } + } + + private Cell getCell(UIDL c) { + int row = c.getIntAttribute("y"); + int col = c.getIntAttribute("x"); + Cell cell = cells[col][row]; + if (cell == null) { + cell = new Cell(c); + cells[col][row] = cell; + } else { + cell.setUidl(c); + } + return cell; + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VHorizontalLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VHorizontalLayout.java new file mode 100644 index 0000000000..042413c850 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VHorizontalLayout.java @@ -0,0 +1,11 @@ +package com.vaadin.terminal.gwt.client.ui; + +public class VHorizontalLayout extends VOrderedLayout { + + public static final String CLASSNAME = "i-horizontallayout"; + + public VHorizontalLayout() { + super(CLASSNAME, ORIENTATION_HORIZONTAL); + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VLabel.java b/src/com/vaadin/terminal/gwt/client/ui/VLabel.java new file mode 100644 index 0000000000..5374f3cfc3 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VLabel.java @@ -0,0 +1,123 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.NodeList; +import com.google.gwt.dom.client.PreElement; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.HTML; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.VTooltip; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +public class VLabel extends HTML implements Paintable { + + public static final String CLASSNAME = "i-label"; + private static final String CLASSNAME_UNDEFINED_WIDTH = "i-label-undef-w"; + + private ApplicationConnection client; + private int verticalPaddingBorder = 0; + private int horizontalPaddingBorder = 0; + + public VLabel() { + super(); + setStyleName(CLASSNAME); + sinkEvents(VTooltip.TOOLTIP_EVENTS); + } + + public VLabel(String text) { + super(text); + setStyleName(CLASSNAME); + sinkEvents(VTooltip.TOOLTIP_EVENTS); + } + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + if (event.getTypeInt() == Event.ONLOAD) { + Util.notifyParentOfSizeChange(this, true); + event.cancelBubble(true); + return; + } + if (client != null) { + client.handleTooltipEvent(event, this); + } + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + + if (client.updateComponent(this, uidl, true)) { + return; + } + + this.client = client; + + boolean sinkOnloads = false; + + final String mode = uidl.getStringAttribute("mode"); + if (mode == null || "text".equals(mode)) { + setText(uidl.getChildString(0)); + } else if ("pre".equals(mode)) { + PreElement preElement = Document.get().createPreElement(); + preElement.setInnerText(uidl.getChildUIDL(0).getChildString(0)); + // clear existing content + setHTML(""); + // add preformatted text to dom + getElement().appendChild(preElement); + } else if ("uidl".equals(mode)) { + setHTML(uidl.getChildrenAsXML()); + } else if ("xhtml".equals(mode)) { + UIDL content = uidl.getChildUIDL(0).getChildUIDL(0); + if (content.getChildCount() > 0) { + setHTML(content.getChildString(0)); + } else { + setHTML(""); + } + sinkOnloads = true; + } else if ("xml".equals(mode)) { + setHTML(uidl.getChildUIDL(0).getChildString(0)); + } else if ("raw".equals(mode)) { + setHTML(uidl.getChildUIDL(0).getChildString(0)); + sinkOnloads = true; + } else { + setText(""); + } + if (sinkOnloads) { + sinkOnloadsForContainedImgs(); + } + } + + private void sinkOnloadsForContainedImgs() { + NodeList images = getElement().getElementsByTagName("img"); + for (int i = 0; i < images.getLength(); i++) { + Element img = images.getItem(i); + DOM.sinkEvents((com.google.gwt.user.client.Element) img, + Event.ONLOAD); + } + + } + + @Override + public void setHeight(String height) { + verticalPaddingBorder = Util.setHeightExcludingPaddingAndBorder(this, + height, verticalPaddingBorder); + } + + @Override + public void setWidth(String width) { + horizontalPaddingBorder = Util.setWidthExcludingPaddingAndBorder(this, + width, horizontalPaddingBorder); + if (width == null || width.equals("")) { + setStyleName(getElement(), CLASSNAME_UNDEFINED_WIDTH, true); + } else { + setStyleName(getElement(), CLASSNAME_UNDEFINED_WIDTH, false); + } + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VLink.java b/src/com/vaadin/terminal/gwt/client/ui/VLink.java new file mode 100644 index 0000000000..6aee7eb63e --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VLink.java @@ -0,0 +1,182 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.ui.ClickListener; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.VTooltip; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +public class VLink extends HTML implements Paintable, ClickListener { + + public static final String CLASSNAME = "i-link"; + + private static final int BORDER_STYLE_DEFAULT = 0; + private static final int BORDER_STYLE_MINIMAL = 1; + private static final int BORDER_STYLE_NONE = 2; + + private String src; + + private String target; + + private int borderStyle = BORDER_STYLE_DEFAULT; + + private boolean enabled; + + private boolean readonly; + + private int targetWidth; + + private int targetHeight; + + private Element errorIndicatorElement; + + private final Element anchor = DOM.createAnchor(); + + private final Element captionElement = DOM.createSpan(); + + private Icon icon; + + private ApplicationConnection client; + + public VLink() { + super(); + getElement().appendChild(anchor); + anchor.appendChild(captionElement); + addClickListener(this); + sinkEvents(VTooltip.TOOLTIP_EVENTS); + setStyleName(CLASSNAME); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + + // Ensure correct implementation, + // but don't let container manage caption etc. + if (client.updateComponent(this, uidl, false)) { + return; + } + + this.client = client; + + enabled = uidl.hasAttribute("disabled") ? false : true; + readonly = uidl.hasAttribute("readonly") ? true : false; + + if (uidl.hasAttribute("name")) { + target = uidl.getStringAttribute("name"); + anchor.setAttribute("target", target); + } + if (uidl.hasAttribute("src")) { + src = client.translateToolkitUri(uidl.getStringAttribute("src")); + anchor.setAttribute("href", src); + } + + if (uidl.hasAttribute("border")) { + if ("none".equals(uidl.getStringAttribute("border"))) { + borderStyle = BORDER_STYLE_NONE; + } else { + borderStyle = BORDER_STYLE_MINIMAL; + } + } else { + borderStyle = BORDER_STYLE_DEFAULT; + } + + targetHeight = uidl.hasAttribute("targetHeight") ? uidl + .getIntAttribute("targetHeight") : -1; + targetWidth = uidl.hasAttribute("targetWidth") ? uidl + .getIntAttribute("targetWidth") : -1; + + // Set link caption + captionElement.setInnerText(uidl.getStringAttribute("caption")); + + // handle error + if (uidl.hasAttribute("error")) { + if (errorIndicatorElement == null) { + errorIndicatorElement = DOM.createDiv(); + DOM.setElementProperty(errorIndicatorElement, "className", + "i-errorindicator"); + } + DOM.insertChild(getElement(), errorIndicatorElement, 0); + } else if (errorIndicatorElement != null) { + DOM.setStyleAttribute(errorIndicatorElement, "display", "none"); + } + + if (uidl.hasAttribute("icon")) { + if (icon == null) { + icon = new Icon(client); + anchor.insertBefore(icon.getElement(), captionElement); + } + icon.setUri(uidl.getStringAttribute("icon")); + } + + } + + public void onClick(Widget sender) { + if (enabled && !readonly) { + if (target == null) { + target = "_self"; + } + String features; + switch (borderStyle) { + case BORDER_STYLE_NONE: + features = "menubar=no,location=no,status=no"; + break; + case BORDER_STYLE_MINIMAL: + features = "menubar=yes,location=no,status=no"; + break; + default: + features = ""; + break; + } + + if (targetWidth > 0) { + features += (features.length() > 0 ? "," : "") + "width=" + + targetWidth; + } + if (targetHeight > 0) { + features += (features.length() > 0 ? "," : "") + "height=" + + targetHeight; + } + + if (features.length() > 0) { + // if 'special features' are set, use window.open(), unless + // a modifier key is held (ctrl to open in new tab etc) + Event e = DOM.eventGetCurrentEvent(); + if (!e.getCtrlKey() && !e.getAltKey() && !e.getShiftKey() + && !e.getMetaKey()) { + Window.open(src, target, features); + e.preventDefault(); + } + } + } + } + + @Override + public void onBrowserEvent(Event event) { + final Element target = DOM.eventGetTarget(event); + if (event.getTypeInt() == Event.ONLOAD) { + Util.notifyParentOfSizeChange(this, true); + } + if (client != null) { + client.handleTooltipEvent(event, this); + } + if (target == captionElement || target == anchor + || (icon != null && target == icon.getElement())) { + super.onBrowserEvent(event); + } + if (!enabled) { + event.preventDefault(); + } + + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VListSelect.java b/src/com/vaadin/terminal/gwt/client/ui/VListSelect.java new file mode 100644 index 0000000000..84443a39e1 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VListSelect.java @@ -0,0 +1,141 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.Iterator; +import java.util.Vector; + +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.ListBox; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.VTooltip; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; + +public class VListSelect extends VOptionGroupBase { + + public static final String CLASSNAME = "i-select"; + + private static final int VISIBLE_COUNT = 10; + + protected TooltipListBox select; + + private int lastSelectedIndex = -1; + + public VListSelect() { + super(new TooltipListBox(true), CLASSNAME); + select = (TooltipListBox) optionsContainer; + select.setSelect(this); + select.addChangeListener(this); + select.addClickListener(this); + select.setStyleName(CLASSNAME + "-select"); + select.setVisibleItemCount(VISIBLE_COUNT); + } + + @Override + protected void buildOptions(UIDL uidl) { + select.setClient(client); + select.setMultipleSelect(isMultiselect()); + select.setEnabled(!isDisabled() && !isReadonly()); + select.clear(); + if (!isMultiselect() && isNullSelectionAllowed() + && !isNullSelectionItemAvailable()) { + // can't unselect last item in singleselect mode + select.addItem("", null); + } + for (final Iterator i = uidl.getChildIterator(); i.hasNext();) { + final UIDL optionUidl = (UIDL) i.next(); + select.addItem(optionUidl.getStringAttribute("caption"), optionUidl + .getStringAttribute("key")); + if (optionUidl.hasAttribute("selected")) { + select.setItemSelected(select.getItemCount() - 1, true); + } + } + if (getRows() > 0) { + select.setVisibleItemCount(getRows()); + } + } + + @Override + protected Object[] getSelectedItems() { + final Vector selectedItemKeys = new Vector(); + for (int i = 0; i < select.getItemCount(); i++) { + if (select.isItemSelected(i)) { + selectedItemKeys.add(select.getValue(i)); + } + } + return selectedItemKeys.toArray(); + } + + @Override + public void onChange(Widget sender) { + final int si = select.getSelectedIndex(); + if (si == -1 && !isNullSelectionAllowed()) { + select.setSelectedIndex(lastSelectedIndex); + } else { + lastSelectedIndex = si; + if (isMultiselect()) { + client.updateVariable(id, "selected", getSelectedItems(), + isImmediate()); + } else { + client.updateVariable(id, "selected", new String[] { "" + + getSelectedItem() }, isImmediate()); + } + } + } + + @Override + public void setHeight(String height) { + select.setHeight(height); + super.setHeight(height); + } + + @Override + public void setWidth(String width) { + select.setWidth(width); + super.setWidth(width); + } + + @Override + protected void setTabIndex(int tabIndex) { + ((TooltipListBox) optionsContainer).setTabIndex(tabIndex); + } + + public void focus() { + select.setFocus(true); + } + +} + +/** + * Extended ListBox to listen tooltip events and forward them to generic + * handler. + */ +class TooltipListBox extends ListBox { + private ApplicationConnection client; + private Paintable pntbl; + + TooltipListBox(boolean isMultiselect) { + super(isMultiselect); + sinkEvents(VTooltip.TOOLTIP_EVENTS); + } + + public void setClient(ApplicationConnection client) { + this.client = client; + } + + public void setSelect(Paintable s) { + pntbl = s; + } + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + if (client != null) { + client.handleTooltipEvent(event, pntbl); + } + } +} \ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/client/ui/VMarginInfo.java b/src/com/vaadin/terminal/gwt/client/ui/VMarginInfo.java new file mode 100644 index 0000000000..961624dcd7 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VMarginInfo.java @@ -0,0 +1,76 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.io.Serializable; + +@SuppressWarnings("serial") +public class VMarginInfo implements Serializable { + + private static final int TOP = 1; + private static final int RIGHT = 2; + private static final int BOTTOM = 4; + private static final int LEFT = 8; + + private int bitMask; + + public VMarginInfo(int bitMask) { + this.bitMask = bitMask; + } + + public VMarginInfo(boolean top, boolean right, boolean bottom, boolean left) { + setMargins(top, right, bottom, left); + } + + public void setMargins(boolean top, boolean right, boolean bottom, + boolean left) { + bitMask = top ? TOP : 0; + bitMask += right ? RIGHT : 0; + bitMask += bottom ? BOTTOM : 0; + bitMask += left ? LEFT : 0; + } + + public void setMargins(VMarginInfo marginInfo) { + bitMask = marginInfo.bitMask; + } + + public boolean hasLeft() { + return (bitMask & LEFT) == LEFT; + } + + public boolean hasRight() { + return (bitMask & RIGHT) == RIGHT; + } + + public boolean hasTop() { + return (bitMask & TOP) == TOP; + } + + public boolean hasBottom() { + return (bitMask & BOTTOM) == BOTTOM; + } + + public int getBitMask() { + return bitMask; + } + + public void setMargins(boolean enabled) { + if (enabled) { + bitMask = TOP + RIGHT + BOTTOM + LEFT; + } else { + bitMask = 0; + } + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof VMarginInfo)) { + return false; + } + + return ((VMarginInfo) obj).bitMask == bitMask; + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java b/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java new file mode 100644 index 0000000000..1a71099370 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java @@ -0,0 +1,644 @@ +package com.vaadin.terminal.gwt.client.ui; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Stack; + +import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.DeferredCommand; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.HasHTML; +import com.google.gwt.user.client.ui.PopupListener; +import com.google.gwt.user.client.ui.PopupPanel; +import com.google.gwt.user.client.ui.UIObject; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; + +public class VMenuBar extends Widget implements Paintable, PopupListener { + + /** Set the CSS class name to allow styling. */ + public static final String CLASSNAME = "i-menubar"; + + /** For server connections **/ + protected String uidlId; + protected ApplicationConnection client; + + protected final VMenuBar hostReference = this; + protected String submenuIcon = null; + protected boolean collapseItems = true; + protected CustomMenuItem moreItem = null; + + // Construct an empty command to be used when the item has no command + // associated + protected static final Command emptyCommand = null; + + /** Widget fields **/ + protected boolean subMenu; + protected ArrayList items; + protected Element containerElement; + protected VToolkitOverlay popup; + protected VMenuBar visibleChildMenu; + protected VMenuBar parentMenu; + protected CustomMenuItem selected; + + public VMenuBar() { + // Create an empty horizontal menubar + this(false); + } + + public VMenuBar(boolean subMenu) { + super(); + setElement(DOM.createDiv()); + + items = new ArrayList(); + popup = null; + visibleChildMenu = null; + + Element table = DOM.createTable(); + Element tbody = DOM.createTBody(); + DOM.appendChild(getElement(), table); + DOM.appendChild(table, tbody); + + if (!subMenu) { + setStyleName(CLASSNAME); + Element tr = DOM.createTR(); + DOM.appendChild(tbody, tr); + containerElement = tr; + } else { + setStyleName(CLASSNAME + "-submenu"); + containerElement = tbody; + } + this.subMenu = subMenu; + + sinkEvents(Event.ONCLICK | Event.ONMOUSEOVER | Event.ONMOUSEOUT); + } + + /** + * This method must be implemented to update the client-side component from + * UIDL data received from server. + * + * This method is called when the page is loaded for the first time, and + * every time UI changes in the component are received from the server. + */ + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + // This call should be made first. Ensure correct implementation, + // and let the containing layout manage caption, etc. + if (client.updateComponent(this, uidl, true)) { + return; + } + + // For future connections + this.client = client; + uidlId = uidl.getId(); + + // Empty the menu every time it receives new information + if (!getItems().isEmpty()) { + clearItems(); + } + + UIDL options = uidl.getChildUIDL(0); + + if (options.hasAttribute("submenuIcon")) { + submenuIcon = client.translateToolkitUri(uidl.getChildUIDL(0) + .getStringAttribute("submenuIcon")); + } else { + submenuIcon = null; + } + + collapseItems = options.getBooleanAttribute("collapseItems"); + + if (collapseItems) { + UIDL moreItemUIDL = options.getChildUIDL(0); + StringBuffer itemHTML = new StringBuffer(); + + if (moreItemUIDL.hasAttribute("icon")) { + itemHTML.append(""); + } + itemHTML.append(moreItemUIDL.getStringAttribute("text")); + + moreItem = new CustomMenuItem(itemHTML.toString(), emptyCommand); + } + + UIDL uidlItems = uidl.getChildUIDL(1); + Iterator itr = uidlItems.getChildIterator(); + Stack> iteratorStack = new Stack>(); + Stack menuStack = new Stack(); + VMenuBar currentMenu = this; + + while (itr.hasNext()) { + UIDL item = (UIDL) itr.next(); + CustomMenuItem currentItem = null; + + String itemText = item.getStringAttribute("text"); + final int itemId = item.getIntAttribute("id"); + + boolean itemHasCommand = item.getBooleanAttribute("command"); + + // Construct html from the text and the optional icon + StringBuffer itemHTML = new StringBuffer(); + + if (item.hasAttribute("icon")) { + itemHTML.append(""); + } + + itemHTML.append(itemText); + + if (currentMenu != this && item.getChildCount() > 0 + && submenuIcon != null) { + itemHTML.append(""); + } + + Command cmd = null; + + if (itemHasCommand) { + // Construct a command that fires onMenuClick(int) with the + // item's id-number + cmd = new Command() { + public void execute() { + hostReference.onMenuClick(itemId); + } + }; + } + + currentItem = currentMenu.addItem(itemHTML.toString(), cmd); + + if (item.getChildCount() > 0) { + menuStack.push(currentMenu); + iteratorStack.push(itr); + itr = item.getChildIterator(); + currentMenu = new VMenuBar(true); + currentItem.setSubMenu(currentMenu); + } + + while (!itr.hasNext() && !iteratorStack.empty()) { + itr = iteratorStack.pop(); + currentMenu = (VMenuBar) menuStack.pop(); + } + }// while + + // we might need to collapse the top-level menu + if (collapseItems) { + int topLevelWidth = 0; + + int ourWidth = getOffsetWidth(); + + int i = 0; + for (; i < getItems().size() && topLevelWidth < ourWidth; i++) { + CustomMenuItem item = (CustomMenuItem) getItems().get(i); + topLevelWidth += item.getOffsetWidth(); + } + + if (topLevelWidth > getOffsetWidth()) { + ArrayList toBeCollapsed = new ArrayList(); + VMenuBar collapsed = new VMenuBar(true); + for (int j = i - 2; j < getItems().size(); j++) { + toBeCollapsed.add(getItems().get(j)); + } + + for (int j = 0; j < toBeCollapsed.size(); j++) { + CustomMenuItem item = (CustomMenuItem) toBeCollapsed.get(j); + removeItem(item); + + // it's ugly, but we have to insert the submenu icon + if (item.getSubMenu() != null && submenuIcon != null) { + StringBuffer itemText = new StringBuffer(item.getHTML()); + itemText.append(""); + item.setHTML(itemText.toString()); + } + + collapsed.addItem(item); + } + + moreItem.setSubMenu(collapsed); + addItem(moreItem); + } + } + }// updateFromUIDL + + /** + * This is called by the items in the menu and it communicates the + * information to the server + * + * @param clickedItemId + * id of the item that was clicked + */ + public void onMenuClick(int clickedItemId) { + // Updating the state to the server can not be done before + // the server connection is known, i.e., before updateFromUIDL() + // has been called. + if (uidlId != null && client != null) { + // Communicate the user interaction parameters to server. This call + // will initiate an AJAX request to the server. + client.updateVariable(uidlId, "clickedId", clickedItemId, true); + } + } + + /** Widget methods **/ + + /** + * Returns a list of items in this menu + */ + public List getItems() { + return items; + } + + /** + * Remove all the items in this menu + */ + public void clearItems() { + Element e = getContainingElement(); + while (DOM.getChildCount(e) > 0) { + DOM.removeChild(e, DOM.getChild(e, 0)); + } + items.clear(); + } + + /** + * Returns the containing element of the menu + * + * @return + */ + public Element getContainingElement() { + return containerElement; + } + + /** + * Returns a new child element to add an item to + * + * @return + */ + public Element getNewChildElement() { + if (subMenu) { + Element tr = DOM.createTR(); + DOM.appendChild(getContainingElement(), tr); + return tr; + } else { + return getContainingElement(); + } + + } + + /** + * Add a new item to this menu + * + * @param html + * items text + * @param cmd + * items command + * @return the item created + */ + public CustomMenuItem addItem(String html, Command cmd) { + CustomMenuItem item = new CustomMenuItem(html, cmd); + addItem(item); + return item; + } + + /** + * Add a new item to this menu + * + * @param item + */ + public void addItem(CustomMenuItem item) { + DOM.appendChild(getNewChildElement(), item.getElement()); + item.setParentMenu(this); + item.setSelected(false); + items.add(item); + } + + /** + * Remove the given item from this menu + * + * @param item + */ + public void removeItem(CustomMenuItem item) { + if (items.contains(item)) { + int index = items.indexOf(item); + Element container = getContainingElement(); + + DOM.removeChild(container, DOM.getChild(container, index)); + items.remove(index); + } + } + + /* + * @see + * com.google.gwt.user.client.ui.Widget#onBrowserEvent(com.google.gwt.user + * .client.Event) + */ + @Override + public void onBrowserEvent(Event e) { + super.onBrowserEvent(e); + + Element targetElement = DOM.eventGetTarget(e); + CustomMenuItem targetItem = null; + for (int i = 0; i < items.size(); i++) { + CustomMenuItem item = (CustomMenuItem) items.get(i); + if (DOM.isOrHasChild(item.getElement(), targetElement)) { + targetItem = item; + } + } + + if (targetItem != null) { + switch (DOM.eventGetType(e)) { + + case Event.ONCLICK: + itemClick(targetItem); + break; + + case Event.ONMOUSEOVER: + itemOver(targetItem); + break; + + case Event.ONMOUSEOUT: + itemOut(targetItem); + break; + } + } + } + + /** + * When an item is clicked + * + * @param item + */ + public void itemClick(CustomMenuItem item) { + if (item.getCommand() != null) { + setSelected(null); + + if (visibleChildMenu != null) { + visibleChildMenu.hideChildren(); + } + + hideParents(); + DeferredCommand.addCommand(item.getCommand()); + + } else { + if (item.getSubMenu() != null + && item.getSubMenu() != visibleChildMenu) { + setSelected(item); + showChildMenu(item); + } + } + } + + /** + * When the user hovers the mouse over the item + * + * @param item + */ + public void itemOver(CustomMenuItem item) { + setSelected(item); + + boolean menuWasVisible = visibleChildMenu != null; + + if (menuWasVisible && visibleChildMenu != item.getSubMenu()) { + popup.hide(); + visibleChildMenu = null; + } + + if (item.getSubMenu() != null && (parentMenu != null || menuWasVisible) + && visibleChildMenu != item.getSubMenu()) { + showChildMenu(item); + } + } + + /** + * When the mouse is moved away from an item + * + * @param item + */ + public void itemOut(CustomMenuItem item) { + if (visibleChildMenu != item.getSubMenu() || visibleChildMenu == null) { + hideChildMenu(item); + setSelected(null); + } + } + + /** + * Shows the child menu of an item. The caller must ensure that the item has + * a submenu. + * + * @param item + */ + public void showChildMenu(CustomMenuItem item) { + popup = new VToolkitOverlay(true, false, true); + popup.setWidget(item.getSubMenu()); + popup.addPopupListener(this); + + if (subMenu) { + popup.setPopupPosition(item.getParentMenu().getAbsoluteLeft() + + item.getParentMenu().getOffsetWidth(), item + .getAbsoluteTop()); + } else { + popup.setPopupPosition(item.getAbsoluteLeft(), item.getParentMenu() + .getAbsoluteTop() + + item.getParentMenu().getOffsetHeight()); + } + + item.getSubMenu().onShow(); + visibleChildMenu = item.getSubMenu(); + item.getSubMenu().setParentMenu(this); + + popup.show(); + } + + /** + * Hides the submenu of an item + * + * @param item + */ + public void hideChildMenu(CustomMenuItem item) { + if (visibleChildMenu != null + && !(visibleChildMenu == item.getSubMenu())) { + popup.hide(); + + } + } + + /** + * When the menu is shown. + */ + public void onShow() { + if (!items.isEmpty()) { + ((CustomMenuItem) items.get(0)).setSelected(true); + } + } + + /** + * Recursively hide all child menus + */ + public void hideChildren() { + if (visibleChildMenu != null) { + visibleChildMenu.hideChildren(); + popup.hide(); + } + } + + /** + * Recursively hide all parent menus + */ + public void hideParents() { + + if (visibleChildMenu != null) { + popup.hide(); + setSelected(null); + } + + if (getParentMenu() != null) { + getParentMenu().hideParents(); + } + } + + /** + * Returns the parent menu of this menu, or null if this is the top-level + * menu + * + * @return + */ + public VMenuBar getParentMenu() { + return parentMenu; + } + + /** + * Set the parent menu of this menu + * + * @param parent + */ + public void setParentMenu(VMenuBar parent) { + parentMenu = parent; + } + + /** + * Returns the currently selected item of this menu, or null if nothing is + * selected + * + * @return + */ + public CustomMenuItem getSelected() { + return selected; + } + + /** + * Set the currently selected item of this menu + * + * @param item + */ + public void setSelected(CustomMenuItem item) { + // If we had something selected, unselect + if (item != selected && selected != null) { + selected.setSelected(false); + } + // If we have a valid selection, select it + if (item != null) { + item.setSelected(true); + } + + selected = item; + } + + /** + * Listener method, fired when this menu is closed + */ + public void onPopupClosed(PopupPanel sender, boolean autoClosed) { + hideChildren(); + if (autoClosed) { + hideParents(); + } + // setSelected(null); + visibleChildMenu = null; + popup = null; + + } + + /** + * + * A class to hold information on menu items + * + */ + private class CustomMenuItem extends UIObject implements HasHTML { + + protected String html = null; + protected Command command = null; + protected VMenuBar subMenu = null; + protected VMenuBar parentMenu = null; + + public CustomMenuItem(String html, Command cmd) { + setElement(DOM.createTD()); + + setHTML(html); + setCommand(cmd); + setSelected(false); + + addStyleName("menuitem"); + } + + public void setSelected(boolean selected) { + if (selected) { + addStyleDependentName("selected"); + } else { + removeStyleDependentName("selected"); + } + } + + /* + * setters and getters for the fields + */ + + public void setSubMenu(VMenuBar subMenu) { + this.subMenu = subMenu; + } + + public VMenuBar getSubMenu() { + return subMenu; + } + + public void setParentMenu(VMenuBar parentMenu) { + this.parentMenu = parentMenu; + } + + public VMenuBar getParentMenu() { + return parentMenu; + } + + public void setCommand(Command command) { + this.command = command; + } + + public Command getCommand() { + return command; + } + + public String getHTML() { + return html; + } + + public void setHTML(String html) { + this.html = html; + DOM.setInnerHTML(getElement(), html); + } + + public String getText() { + return html; + } + + public void setText(String text) { + setHTML(text); + + } + } + +}// class VMenuBar diff --git a/src/com/vaadin/terminal/gwt/client/ui/VNativeSelect.java b/src/com/vaadin/terminal/gwt/client/ui/VNativeSelect.java new file mode 100644 index 0000000000..9623c88719 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VNativeSelect.java @@ -0,0 +1,111 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.Iterator; +import java.util.Vector; + +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +public class VNativeSelect extends VOptionGroupBase implements Field { + + public static final String CLASSNAME = "i-select"; + + protected TooltipListBox select; + + public VNativeSelect() { + super(new TooltipListBox(false), CLASSNAME); + select = (TooltipListBox) optionsContainer; + select.setSelect(this); + select.setVisibleItemCount(1); + select.addChangeListener(this); + select.setStyleName(CLASSNAME + "-select"); + + } + + @Override + protected void buildOptions(UIDL uidl) { + select.setClient(client); + select.setEnabled(!isDisabled() && !isReadonly()); + select.clear(); + if (isNullSelectionAllowed() && !isNullSelectionItemAvailable()) { + // can't unselect last item in singleselect mode + select.addItem("", null); + } + boolean selected = false; + for (final Iterator i = uidl.getChildIterator(); i.hasNext();) { + final UIDL optionUidl = (UIDL) i.next(); + select.addItem(optionUidl.getStringAttribute("caption"), optionUidl + .getStringAttribute("key")); + if (optionUidl.hasAttribute("selected")) { + select.setItemSelected(select.getItemCount() - 1, true); + selected = true; + } + } + if (!selected && !isNullSelectionAllowed()) { + // null-select not allowed, but value not selected yet; add null and + // remove when something is selected + select.insertItem("", null, 0); + select.setItemSelected(0, true); + } + if (BrowserInfo.get().isIE6()) { + // lazy size change - IE6 uses naive dropdown that does not have a + // proper size yet + Util.notifyParentOfSizeChange(this, true); + } + } + + @Override + protected Object[] getSelectedItems() { + final Vector selectedItemKeys = new Vector(); + for (int i = 0; i < select.getItemCount(); i++) { + if (select.isItemSelected(i)) { + selectedItemKeys.add(select.getValue(i)); + } + } + return selectedItemKeys.toArray(); + } + + @Override + public void onChange(Widget sender) { + + if (select.isMultipleSelect()) { + client.updateVariable(id, "selected", getSelectedItems(), + isImmediate()); + } else { + client.updateVariable(id, "selected", new String[] { "" + + getSelectedItem() }, isImmediate()); + } + if (!isNullSelectionAllowed() && "null".equals(select.getValue(0))) { + // remove temporary empty item + select.removeItem(0); + } + } + + @Override + public void setHeight(String height) { + select.setHeight(height); + super.setHeight(height); + } + + @Override + public void setWidth(String width) { + select.setWidth(width); + super.setWidth(width); + } + + @Override + protected void setTabIndex(int tabIndex) { + ((TooltipListBox) optionsContainer).setTabIndex(tabIndex); + } + + public void focus() { + select.setFocus(true); + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VNotification.java b/src/com/vaadin/terminal/gwt/client/ui/VNotification.java new file mode 100644 index 0000000000..2c81cd788c --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VNotification.java @@ -0,0 +1,323 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.ArrayList; +import java.util.Date; +import java.util.EventObject; +import java.util.Iterator; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.BrowserInfo; + +public class VNotification extends VToolkitOverlay { + + public static final int CENTERED = 1; + public static final int CENTERED_TOP = 2; + public static final int CENTERED_BOTTOM = 3; + public static final int TOP_LEFT = 4; + public static final int TOP_RIGHT = 5; + public static final int BOTTOM_LEFT = 6; + public static final int BOTTOM_RIGHT = 7; + + public static final int DELAY_FOREVER = -1; + public static final int DELAY_NONE = 0; + + private static final String STYLENAME = "i-Notification"; + private static final int mouseMoveThreshold = 7; + private static final int Z_INDEX_BASE = 20000; + public static final String STYLE_SYSTEM = "system"; + private static final int FADE_ANIMATION_INTERVAL = 50; // == 20 fps + + private int startOpacity = 90; + private int fadeMsec = 400; + private int delayMsec = 1000; + + private Timer fader; + private Timer delay; + + private int x = -1; + private int y = -1; + + private String temporaryStyle; + + private ArrayList listeners; + + public VNotification() { + setStylePrimaryName(STYLENAME); + sinkEvents(Event.ONCLICK); + DOM.setStyleAttribute(getElement(), "zIndex", "" + Z_INDEX_BASE); + } + + public VNotification(int delayMsec) { + this(); + this.delayMsec = delayMsec; + } + + public VNotification(int delayMsec, int fadeMsec, int startOpacity) { + this(delayMsec); + this.fadeMsec = fadeMsec; + this.startOpacity = startOpacity; + } + + public void startDelay() { + DOM.removeEventPreview(this); + if (delayMsec > 0) { + if (delay == null) { + delay = new Timer() { + @Override + public void run() { + fade(); + } + }; + delay.schedule(delayMsec); + } + } else if (delayMsec == 0) { + fade(); + } + } + + @Override + public void show() { + show(CENTERED); + } + + public void show(String style) { + show(CENTERED, style); + } + + public void show(int position) { + show(position, null); + } + + public void show(Widget widget, int position, String style) { + setWidget(widget); + show(position, style); + } + + public void show(String html, int position, String style) { + setWidget(new HTML(html)); + show(position, style); + } + + public void show(int position, String style) { + setOpacity(getElement(), startOpacity); + if (style != null) { + temporaryStyle = style; + addStyleName(style); + } + super.show(); + setPosition(position); + } + + @Override + public void hide() { + DOM.removeEventPreview(this); + cancelDelay(); + cancelFade(); + if (temporaryStyle != null) { + removeStyleName(temporaryStyle); + temporaryStyle = null; + } + super.hide(); + fireEvent(new HideEvent(this)); + } + + public void fade() { + DOM.removeEventPreview(this); + cancelDelay(); + fader = new Timer() { + private final long start = new Date().getTime(); + + @Override + public void run() { + /* + * To make animation smooth, don't count that event happens on + * time. Reduce opacity according to the actual time spent + * instead of fixed decrement. + */ + long now = new Date().getTime(); + long timeEplaced = now - start; + float remainingFraction = 1 - timeEplaced / (float) fadeMsec; + int opacity = (int) (startOpacity * remainingFraction); + if (opacity <= 0) { + cancel(); + hide(); + if (BrowserInfo.get().isOpera()) { + // tray notification on opera needs to explicitly define + // size, reset it + DOM.setStyleAttribute(getElement(), "width", ""); + DOM.setStyleAttribute(getElement(), "height", ""); + } + } else { + setOpacity(getElement(), opacity); + } + } + }; + fader.scheduleRepeating(FADE_ANIMATION_INTERVAL); + } + + public void setPosition(int position) { + final Element el = getElement(); + DOM.setStyleAttribute(el, "top", ""); + DOM.setStyleAttribute(el, "left", ""); + DOM.setStyleAttribute(el, "bottom", ""); + DOM.setStyleAttribute(el, "right", ""); + switch (position) { + case TOP_LEFT: + DOM.setStyleAttribute(el, "top", "0px"); + DOM.setStyleAttribute(el, "left", "0px"); + break; + case TOP_RIGHT: + DOM.setStyleAttribute(el, "top", "0px"); + DOM.setStyleAttribute(el, "right", "0px"); + break; + case BOTTOM_RIGHT: + DOM.setStyleAttribute(el, "position", "absolute"); + if (BrowserInfo.get().isOpera()) { + // tray notification on opera needs explicitly defined size + DOM.setStyleAttribute(el, "width", getOffsetWidth() + "px"); + DOM.setStyleAttribute(el, "height", getOffsetHeight() + "px"); + } + DOM.setStyleAttribute(el, "bottom", "0px"); + DOM.setStyleAttribute(el, "right", "0px"); + break; + case BOTTOM_LEFT: + DOM.setStyleAttribute(el, "bottom", "0px"); + DOM.setStyleAttribute(el, "left", "0px"); + break; + case CENTERED_TOP: + center(); + DOM.setStyleAttribute(el, "top", "0px"); + break; + case CENTERED_BOTTOM: + center(); + DOM.setStyleAttribute(el, "top", ""); + DOM.setStyleAttribute(el, "bottom", "0px"); + break; + default: + case CENTERED: + center(); + break; + } + } + + private void cancelFade() { + if (fader != null) { + fader.cancel(); + fader = null; + } + } + + private void cancelDelay() { + if (delay != null) { + delay.cancel(); + delay = null; + } + } + + private void setOpacity(Element el, int opacity) { + DOM.setStyleAttribute(el, "opacity", "" + (opacity / 100.0)); + if (BrowserInfo.get().isIE()) { + DOM.setStyleAttribute(el, "filter", "Alpha(opacity=" + opacity + + ")"); + } + } + + @Override + public void onBrowserEvent(Event event) { + DOM.removeEventPreview(this); + if (fader == null) { + fade(); + } + } + + @Override + public boolean onEventPreview(Event event) { + int type = DOM.eventGetType(event); + // "modal" + if (delayMsec == -1 || temporaryStyle == STYLE_SYSTEM) { + if (type == Event.ONCLICK) { + if (DOM.isOrHasChild(getElement(), DOM.eventGetTarget(event))) { + fade(); + return false; + } + } + if (temporaryStyle == STYLE_SYSTEM) { + return true; + } else { + return false; + } + } + // default + switch (type) { + case Event.ONMOUSEMOVE: + + if (x < 0) { + x = DOM.eventGetClientX(event); + y = DOM.eventGetClientY(event); + } else if (Math.abs(DOM.eventGetClientX(event) - x) > mouseMoveThreshold + || Math.abs(DOM.eventGetClientY(event) - y) > mouseMoveThreshold) { + startDelay(); + } + break; + case Event.ONMOUSEDOWN: + case Event.ONMOUSEWHEEL: + case Event.ONSCROLL: + startDelay(); + break; + case Event.ONKEYDOWN: + if (event.getRepeat()) { + return true; + } + startDelay(); + break; + default: + break; + } + return true; + } + + public void addEventListener(EventListener listener) { + if (listeners == null) { + listeners = new ArrayList(); + } + listeners.add(listener); + } + + public void removeEventListener(EventListener listener) { + if (listeners == null) { + return; + } + listeners.remove(listener); + } + + private void fireEvent(HideEvent event) { + if (listeners != null) { + for (Iterator it = listeners.iterator(); it + .hasNext();) { + EventListener l = it.next(); + l.notificationHidden(event); + } + } + } + + public class HideEvent extends EventObject { + private static final long serialVersionUID = 4428671753988459560L; + + public HideEvent(Object source) { + super(source); + } + } + + public interface EventListener extends java.util.EventListener { + public void notificationHidden(HideEvent event); + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VOptionGroup.java b/src/com/vaadin/terminal/gwt/client/ui/VOptionGroup.java new file mode 100644 index 0000000000..8d2f41f2ae --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VOptionGroup.java @@ -0,0 +1,102 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import com.google.gwt.user.client.ui.CheckBox; +import com.google.gwt.user.client.ui.HasFocus; +import com.google.gwt.user.client.ui.Panel; +import com.google.gwt.user.client.ui.RadioButton; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.UIDL; + +public class VOptionGroup extends VOptionGroupBase { + + public static final String CLASSNAME = "i-select-optiongroup"; + + private final Panel panel; + + private final Map optionsToKeys; + + public VOptionGroup() { + super(CLASSNAME); + panel = (Panel) optionsContainer; + optionsToKeys = new HashMap(); + } + + /* + * Return true if no elements were changed, false otherwise. + */ + @Override + protected void buildOptions(UIDL uidl) { + panel.clear(); + for (final Iterator it = uidl.getChildIterator(); it.hasNext();) { + final UIDL opUidl = (UIDL) it.next(); + CheckBox op; + if (isMultiselect()) { + op = new VCheckBox(); + op.setText(opUidl.getStringAttribute("caption")); + } else { + op = new RadioButton(id, opUidl.getStringAttribute("caption")); + op.setStyleName("i-radiobutton"); + } + op.addStyleName(CLASSNAME_OPTION); + op.setChecked(opUidl.getBooleanAttribute("selected")); + op.setEnabled(!opUidl.getBooleanAttribute("disabled") + && !isReadonly() && !isDisabled()); + op.addClickListener(this); + optionsToKeys.put(op, opUidl.getStringAttribute("key")); + panel.add(op); + } + } + + @Override + protected Object[] getSelectedItems() { + return selectedKeys.toArray(); + } + + @Override + public void onClick(Widget sender) { + super.onClick(sender); + if (sender instanceof CheckBox) { + final boolean selected = ((CheckBox) sender).isChecked(); + final String key = (String) optionsToKeys.get(sender); + if (!isMultiselect()) { + selectedKeys.clear(); + } + if (selected) { + selectedKeys.add(key); + } else { + selectedKeys.remove(key); + } + client.updateVariable(id, "selected", getSelectedItems(), + isImmediate()); + } + } + + @Override + protected void setTabIndex(int tabIndex) { + for (Iterator iterator = panel.iterator(); iterator.hasNext();) { + if (isMultiselect()) { + VCheckBox cb = (VCheckBox) iterator.next(); + cb.setTabIndex(tabIndex); + } else { + RadioButton rb = (RadioButton) iterator.next(); + rb.setTabIndex(tabIndex); + } + } + } + + public void focus() { + Iterator iterator = panel.iterator(); + if (iterator.hasNext()) { + ((HasFocus) iterator.next()).setFocus(true); + } + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VOptionGroupBase.java b/src/com/vaadin/terminal/gwt/client/ui/VOptionGroupBase.java new file mode 100644 index 0000000000..906949506a --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VOptionGroupBase.java @@ -0,0 +1,229 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.Set; + +import com.google.gwt.user.client.ui.ChangeListener; +import com.google.gwt.user.client.ui.ClickListener; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.KeyboardListener; +import com.google.gwt.user.client.ui.Panel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.Focusable; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; + +abstract class VOptionGroupBase extends Composite implements Paintable, Field, + ClickListener, ChangeListener, KeyboardListener, Focusable { + + public static final String CLASSNAME_OPTION = "i-select-option"; + + protected ApplicationConnection client; + + protected String id; + + protected Set selectedKeys; + + private boolean immediate; + + private boolean multiselect; + + private boolean disabled; + + private boolean readonly; + + private int cols = 0; + + private int rows = 0; + + private boolean nullSelectionAllowed = true; + + private boolean nullSelectionItemAvailable = false; + + /** + * Widget holding the different options (e.g. ListBox or Panel for radio + * buttons) (optional, fallbacks to container Panel) + */ + protected Widget optionsContainer; + + /** + * Panel containing the component + */ + private final Panel container; + + private VTextField newItemField; + + private VButton newItemButton; + + public VOptionGroupBase(String classname) { + container = new FlowPanel(); + initWidget(container); + optionsContainer = container; + container.setStyleName(classname); + immediate = false; + multiselect = false; + } + + /* + * Call this if you wish to specify your own container for the option + * elements (e.g. SELECT) + */ + public VOptionGroupBase(Widget w, String classname) { + this(classname); + optionsContainer = w; + container.add(optionsContainer); + } + + protected boolean isImmediate() { + return immediate; + } + + protected boolean isMultiselect() { + return multiselect; + } + + protected boolean isDisabled() { + return disabled; + } + + protected boolean isReadonly() { + return readonly; + } + + protected boolean isNullSelectionAllowed() { + return nullSelectionAllowed; + } + + protected boolean isNullSelectionItemAvailable() { + return nullSelectionItemAvailable; + } + + /** + * @return "cols" specified in uidl, 0 if not specified + */ + protected int getColumns() { + return cols; + } + + /** + * @return "rows" specified in uidl, 0 if not specified + */ + + protected int getRows() { + return rows; + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + this.client = client; + id = uidl.getId(); + + if (client.updateComponent(this, uidl, true)) { + return; + } + + selectedKeys = uidl.getStringArrayVariableAsSet("selected"); + + readonly = uidl.getBooleanAttribute("readonly"); + disabled = uidl.getBooleanAttribute("disabled"); + multiselect = "multi".equals(uidl.getStringAttribute("selectmode")); + immediate = uidl.getBooleanAttribute("immediate"); + nullSelectionAllowed = uidl.getBooleanAttribute("nullselect"); + nullSelectionItemAvailable = uidl.getBooleanAttribute("nullselectitem"); + + cols = uidl.getIntAttribute("cols"); + rows = uidl.getIntAttribute("rows"); + + final UIDL ops = uidl.getChildUIDL(0); + + if (getColumns() > 0) { + container.setWidth(getColumns() + "em"); + if (container != optionsContainer) { + optionsContainer.setWidth("100%"); + } + } + + buildOptions(ops); + + if (uidl.getBooleanAttribute("allownewitem")) { + if (newItemField == null) { + newItemButton = new VButton(); + newItemButton.setText("+"); + newItemButton.setWidth("1.5em"); + newItemButton.addClickListener(this); + newItemField = new VTextField(); + newItemField.addKeyboardListener(this); + // newItemField.setColumns(16); + if (getColumns() > 0) { + newItemField.setWidth((getColumns() - 2) + "em"); + } + } + newItemField.setEnabled(!disabled && !readonly); + newItemButton.setEnabled(!disabled && !readonly); + + if (newItemField == null || newItemField.getParent() != container) { + container.add(newItemField); + container.add(newItemButton); + } + } else if (newItemField != null) { + container.remove(newItemField); + container.remove(newItemButton); + } + + setTabIndex(uidl.hasAttribute("tabindex") ? uidl + .getIntAttribute("tabindex") : 0); + + } + + abstract protected void setTabIndex(int tabIndex); + + public void onClick(Widget sender) { + if (sender == newItemButton && !newItemField.getText().equals("")) { + client.updateVariable(id, "newitem", newItemField.getText(), true); + newItemField.setText(""); + } + } + + public void onChange(Widget sender) { + if (multiselect) { + client + .updateVariable(id, "selected", getSelectedItems(), + immediate); + } else { + client.updateVariable(id, "selected", new String[] { "" + + getSelectedItem() }, immediate); + } + } + + public void onKeyPress(Widget sender, char keyCode, int modifiers) { + if (sender == newItemField && keyCode == KeyboardListener.KEY_ENTER) { + newItemButton.click(); + } + } + + public void onKeyUp(Widget sender, char keyCode, int modifiers) { + // Ignore, subclasses may override + } + + public void onKeyDown(Widget sender, char keyCode, int modifiers) { + // Ignore, subclasses may override + } + + protected abstract void buildOptions(UIDL uidl); + + protected abstract Object[] getSelectedItems(); + + protected Object getSelectedItem() { + final Object[] sel = getSelectedItems(); + if (sel.length > 0) { + return sel[0]; + } else { + return null; + } + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VOrderedLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VOrderedLayout.java new file mode 100644 index 0000000000..19fadb1c11 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VOrderedLayout.java @@ -0,0 +1,876 @@ +package com.vaadin.terminal.gwt.client.ui; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Set; + +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.RenderSpace; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; +import com.vaadin.terminal.gwt.client.RenderInformation.FloatSize; +import com.vaadin.terminal.gwt.client.RenderInformation.Size; +import com.vaadin.terminal.gwt.client.ui.layout.CellBasedLayout; +import com.vaadin.terminal.gwt.client.ui.layout.ChildComponentContainer; + +public class VOrderedLayout extends CellBasedLayout { + + public static final String CLASSNAME = "i-orderedlayout"; + + private int orientation; + + // Can be removed once OrderedLayout is removed + private boolean allowOrientationUpdate = false; + + /** + * Size of the layout excluding any margins. + */ + private Size activeLayoutSize = new Size(0, 0); + + private boolean isRendering = false; + + private String width = ""; + + private boolean sizeHasChangedDuringRendering = false; + + public VOrderedLayout() { + this(CLASSNAME, ORIENTATION_VERTICAL); + allowOrientationUpdate = true; + } + + protected VOrderedLayout(String className, int orientation) { + setStyleName(className); + this.orientation = orientation; + + STYLENAME_SPACING = className + "-spacing"; + STYLENAME_MARGIN_TOP = className + "-margin-top"; + STYLENAME_MARGIN_RIGHT = className + "-margin-right"; + STYLENAME_MARGIN_BOTTOM = className + "-margin-bottom"; + STYLENAME_MARGIN_LEFT = className + "-margin-left"; + + } + + @Override + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + isRendering = true; + super.updateFromUIDL(uidl, client); + + // Only non-cached, visible UIDL:s can introduce changes + if (uidl.getBooleanAttribute("cached") + || uidl.getBooleanAttribute("invisible")) { + isRendering = false; + return; + } + + if (allowOrientationUpdate) { + handleOrientationUpdate(uidl); + } + + // IStopWatch w = new IStopWatch("OrderedLayout.updateFromUIDL"); + + ArrayList uidlWidgets = new ArrayList(uidl + .getChildCount()); + ArrayList relativeSizeComponents = new ArrayList(); + ArrayList relativeSizeComponentUIDL = new ArrayList(); + + int pos = 0; + for (final Iterator it = uidl.getChildIterator(); it.hasNext();) { + final UIDL childUIDL = it.next(); + final Paintable child = client.getPaintable(childUIDL); + Widget widget = (Widget) child; + + // Create container for component + ChildComponentContainer childComponentContainer = getComponentContainer(widget); + + if (childComponentContainer == null) { + // This is a new component + childComponentContainer = createChildContainer(widget); + } + + addOrMoveChild(childComponentContainer, pos++); + + /* + * Components which are to be expanded in the same orientation as + * the layout are rendered later when it is clear how much space + * they can use + */ + if (!Util.isCached(childUIDL)) { + FloatSize relativeSize = Util.parseRelativeSize(childUIDL); + childComponentContainer.setRelativeSize(relativeSize); + } + + if (childComponentContainer.isComponentRelativeSized(orientation)) { + relativeSizeComponents.add(childComponentContainer); + relativeSizeComponentUIDL.add(childUIDL); + } else { + if (isDynamicWidth()) { + childComponentContainer.renderChild(childUIDL, client, 0); + } else { + childComponentContainer.renderChild(childUIDL, client, + activeLayoutSize.getWidth()); + } + if (sizeHasChangedDuringRendering && Util.isCached(childUIDL)) { + // notify cached relative sized component about size + // chance + client.handleComponentRelativeSize(childComponentContainer + .getWidget()); + } + } + + uidlWidgets.add(widget); + + } + + // w.mark("Rendering of " + // + (uidlWidgets.size() - relativeSizeComponents.size()) + // + " absolute size components done"); + + /* + * Remove any children after pos. These are the ones that previously + * were in the layout but have now been removed + */ + removeChildrenAfter(pos); + + // w.mark("Old children removed"); + + /* Fetch alignments and expand ratio from UIDL */ + updateAlignmentsAndExpandRatios(uidl, uidlWidgets); + // w.mark("Alignments and expand ratios updated"); + + /* Fetch widget sizes from rendered components */ + updateWidgetSizes(); + // w.mark("Widget sizes updated"); + + recalculateLayout(); + // w.mark("Layout size calculated (" + activeLayoutSize + + // ") offsetSize: " + // + getOffsetWidth() + "," + getOffsetHeight()); + + /* Render relative size components */ + for (int i = 0; i < relativeSizeComponents.size(); i++) { + ChildComponentContainer childComponentContainer = relativeSizeComponents + .get(i); + UIDL childUIDL = relativeSizeComponentUIDL.get(i); + + if (isDynamicWidth()) { + childComponentContainer.renderChild(childUIDL, client, 0); + } else { + childComponentContainer.renderChild(childUIDL, client, + activeLayoutSize.getWidth()); + } + + if (Util.isCached(childUIDL)) { + /* + * We must update the size of the relative sized component if + * the expand ratio or something else in the layout changes + * which affects the size of a relative sized component + */ + client.handleComponentRelativeSize(childComponentContainer + .getWidget()); + } + + // childComponentContainer.updateWidgetSize(); + } + + // w.mark("Rendering of " + (relativeSizeComponents.size()) + // + " relative size components done"); + + /* Fetch widget sizes for relative size components */ + for (ChildComponentContainer childComponentContainer : widgetToComponentContainer + .values()) { + + /* Update widget size from DOM */ + childComponentContainer.updateWidgetSize(); + } + + // w.mark("Widget sizes updated"); + + /* + * Components with relative size in main direction may affect the layout + * size in the other direction + */ + if ((isHorizontal() && isDynamicHeight()) + || (isVertical() && isDynamicWidth())) { + layoutSizeMightHaveChanged(); + } + // w.mark("Layout dimensions updated"); + + /* Update component spacing */ + updateContainerMargins(); + + /* + * Update component sizes for components with relative size in non-main + * direction + */ + if (updateRelativeSizesInNonMainDirection()) { + // Sizes updated - might affect the other dimension so we need to + // recheck the widget sizes and recalculate layout dimensions + updateWidgetSizes(); + layoutSizeMightHaveChanged(); + } + calculateAlignments(); + // w.mark("recalculateComponentSizesAndAlignments done"); + + setRootSize(); + + if (BrowserInfo.get().isIE()) { + /* + * This should fix the issue with padding not always taken into + * account for the containers leading to no spacing between + * elements. + */ + root.getStyle().setProperty("zoom", "1"); + } + + // w.mark("runDescendentsLayout done"); + isRendering = false; + sizeHasChangedDuringRendering = false; + } + + private void layoutSizeMightHaveChanged() { + Size oldSize = new Size(activeLayoutSize.getWidth(), activeLayoutSize + .getHeight()); + calculateLayoutDimensions(); + + /* + * If layout dimension changes we must also update container sizes + */ + if (!oldSize.equals(activeLayoutSize)) { + calculateContainerSize(); + } + } + + private void updateWidgetSizes() { + for (ChildComponentContainer childComponentContainer : widgetToComponentContainer + .values()) { + + /* + * Update widget size from DOM + */ + childComponentContainer.updateWidgetSize(); + } + } + + private void recalculateLayout() { + + /* Calculate space for relative size components */ + int spaceForExpansion = calculateLayoutDimensions(); + + if (!widgetToComponentContainer.isEmpty()) { + /* Divide expansion space between component containers */ + expandComponentContainers(spaceForExpansion); + + /* Update container sizes */ + calculateContainerSize(); + } + + } + + private void expandComponentContainers(int spaceForExpansion) { + int remaining = spaceForExpansion; + for (ChildComponentContainer childComponentContainer : widgetToComponentContainer + .values()) { + remaining -= childComponentContainer.expand(orientation, + spaceForExpansion); + } + + if (remaining > 0) { + // Some left-over pixels due to rounding errors + + // Add one pixel to each container until there are no pixels left + + Iterator widgetIterator = iterator(); + while (widgetIterator.hasNext() && remaining-- > 0) { + ChildComponentContainer childComponentContainer = (ChildComponentContainer) widgetIterator + .next(); + childComponentContainer.expandExtra(orientation, 1); + } + } + + } + + private void handleOrientationUpdate(UIDL uidl) { + int newOrientation = ORIENTATION_VERTICAL; + if ("horizontal".equals(uidl.getStringAttribute("orientation"))) { + newOrientation = ORIENTATION_HORIZONTAL; + } + + if (orientation != newOrientation) { + orientation = newOrientation; + + for (ChildComponentContainer childComponentContainer : widgetToComponentContainer + .values()) { + childComponentContainer.setOrientation(orientation); + } + } + + } + + /** + * Updated components with relative height in horizontal layouts and + * components with relative width in vertical layouts. This is only needed + * if the height (horizontal layout) or width (vertical layout) has not been + * specified. + */ + private boolean updateRelativeSizesInNonMainDirection() { + int updateDirection = 1 - orientation; + if ((updateDirection == ORIENTATION_HORIZONTAL && !isDynamicWidth()) + || (updateDirection == ORIENTATION_VERTICAL && !isDynamicHeight())) { + return false; + } + + boolean updated = false; + for (ChildComponentContainer componentContainer : widgetToComponentContainer + .values()) { + if (componentContainer.isComponentRelativeSized(updateDirection)) { + client.handleComponentRelativeSize(componentContainer + .getWidget()); + } + + updated = true; + } + + return updated; + } + + private int calculateLayoutDimensions() { + int summedWidgetWidth = 0; + int summedWidgetHeight = 0; + + int maxWidgetWidth = 0; + int maxWidgetHeight = 0; + + // Calculate layout dimensions from component dimensions + for (ChildComponentContainer childComponentContainer : widgetToComponentContainer + .values()) { + + int widgetHeight = 0; + int widgetWidth = 0; + if (childComponentContainer.isComponentRelativeSized(orientation)) { + if (orientation == ORIENTATION_HORIZONTAL) { + widgetHeight = getWidgetHeight(childComponentContainer); + } else { + widgetWidth = getWidgetWidth(childComponentContainer); + } + } else { + widgetWidth = getWidgetWidth(childComponentContainer); + widgetHeight = getWidgetHeight(childComponentContainer); + } + + summedWidgetWidth += widgetWidth; + summedWidgetHeight += widgetHeight; + + maxWidgetHeight = Math.max(maxWidgetHeight, widgetHeight); + maxWidgetWidth = Math.max(maxWidgetWidth, widgetWidth); + } + + if (isHorizontal()) { + summedWidgetWidth += activeSpacing.hSpacing + * (widgetToComponentContainer.size() - 1); + } else { + summedWidgetHeight += activeSpacing.vSpacing + * (widgetToComponentContainer.size() - 1); + } + + Size layoutSize = updateLayoutDimensions(summedWidgetWidth, + summedWidgetHeight, maxWidgetWidth, maxWidgetHeight); + + int remainingSpace; + if (isHorizontal()) { + remainingSpace = layoutSize.getWidth() - summedWidgetWidth; + } else { + remainingSpace = layoutSize.getHeight() - summedWidgetHeight; + } + if (remainingSpace < 0) { + remainingSpace = 0; + } + + // ApplicationConnection.getConsole().log( + // "Layout size: " + activeLayoutSize); + return remainingSpace; + } + + private int getWidgetHeight(ChildComponentContainer childComponentContainer) { + Size s = childComponentContainer.getWidgetSize(); + return s.getHeight() + + childComponentContainer.getCaptionHeightAboveComponent(); + } + + private int getWidgetWidth(ChildComponentContainer childComponentContainer) { + Size s = childComponentContainer.getWidgetSize(); + int widgetWidth = s.getWidth() + + childComponentContainer.getCaptionWidthAfterComponent(); + + /* + * If the component does not have a specified size in the main direction + * the caption may determine the space used by the component + */ + if (!childComponentContainer.widgetHasSizeSpecified(orientation)) { + int captionWidth = childComponentContainer + .getCaptionRequiredWidth(); + + if (captionWidth > widgetWidth) { + widgetWidth = captionWidth; + } + } + + return widgetWidth; + } + + private void calculateAlignments() { + int w = 0; + int h = 0; + + if (isHorizontal()) { + // HORIZONTAL + h = activeLayoutSize.getHeight(); + if (!isDynamicWidth()) { + w = -1; + } + + } else { + // VERTICAL + w = activeLayoutSize.getWidth(); + if (!isDynamicHeight()) { + h = -1; + } + } + + for (ChildComponentContainer childComponentContainer : widgetToComponentContainer + .values()) { + childComponentContainer.updateAlignments(w, h); + } + + } + + private void calculateContainerSize() { + + /* + * Container size here means the size the container gets from the + * component. The expansion size is not include in this but taken + * separately into account. + */ + int height = 0, width = 0; + Iterator widgetIterator = iterator(); + if (isHorizontal()) { + height = activeLayoutSize.getHeight(); + int availableWidth = activeLayoutSize.getWidth(); + boolean first = true; + while (widgetIterator.hasNext()) { + ChildComponentContainer childComponentContainer = (ChildComponentContainer) widgetIterator + .next(); + if (!childComponentContainer + .isComponentRelativeSized(ORIENTATION_HORIZONTAL)) { + /* + * Only components with non-relative size in the main + * direction has a container size + */ + width = childComponentContainer.getWidgetSize().getWidth() + + childComponentContainer + .getCaptionWidthAfterComponent(); + + /* + * If the component does not have a specified size in the + * main direction the caption may determine the space used + * by the component + */ + if (!childComponentContainer + .widgetHasSizeSpecified(orientation)) { + int captionWidth = childComponentContainer + .getCaptionRequiredWidth(); + // ApplicationConnection.getConsole().log( + // "Component width: " + width + // + ", caption width: " + captionWidth); + if (captionWidth > width) { + width = captionWidth; + } + } + } else { + width = 0; + } + + if (!isDynamicWidth()) { + if (availableWidth == 0) { + /* + * Let the overflowing components overflow. IE has + * problems with zero sizes. + */ + // width = 0; + // height = 0; + } else if (width > availableWidth) { + width = availableWidth; + + if (!first) { + width -= activeSpacing.hSpacing; + } + availableWidth = 0; + } else { + availableWidth -= width; + if (!first) { + availableWidth -= activeSpacing.hSpacing; + } + } + + first = false; + } + + childComponentContainer.setContainerSize(width, height); + } + } else { + width = activeLayoutSize.getWidth(); + while (widgetIterator.hasNext()) { + ChildComponentContainer childComponentContainer = (ChildComponentContainer) widgetIterator + .next(); + + if (!childComponentContainer + .isComponentRelativeSized(ORIENTATION_VERTICAL)) { + /* + * Only components with non-relative size in the main + * direction has a container size + */ + height = childComponentContainer.getWidgetSize() + .getHeight() + + childComponentContainer + .getCaptionHeightAboveComponent(); + } else { + height = 0; + } + + childComponentContainer.setContainerSize(width, height); + } + + } + + } + + private Size updateLayoutDimensions(int totalComponentWidth, + int totalComponentHeight, int maxComponentWidth, + int maxComponentHeight) { + + /* Only need to calculate dynamic dimensions */ + if (!isDynamicHeight() && !isDynamicWidth()) { + return activeLayoutSize; + } + + int activeLayoutWidth = 0; + int activeLayoutHeight = 0; + + // Update layout dimensions + if (isHorizontal()) { + // Horizontal + if (isDynamicWidth()) { + activeLayoutWidth = totalComponentWidth; + } + + if (isDynamicHeight()) { + activeLayoutHeight = maxComponentHeight; + } + + } else { + // Vertical + if (isDynamicWidth()) { + activeLayoutWidth = maxComponentWidth; + } + + if (isDynamicHeight()) { + activeLayoutHeight = totalComponentHeight; + } + } + + if (isDynamicWidth()) { + setActiveLayoutWidth(activeLayoutWidth); + setOuterLayoutWidth(activeLayoutSize.getWidth()); + } + + if (isDynamicHeight()) { + setActiveLayoutHeight(activeLayoutHeight); + setOuterLayoutHeight(activeLayoutSize.getHeight()); + } + + return activeLayoutSize; + } + + private void setActiveLayoutWidth(int activeLayoutWidth) { + if (activeLayoutWidth < 0) { + activeLayoutWidth = 0; + } + activeLayoutSize.setWidth(activeLayoutWidth); + } + + private void setActiveLayoutHeight(int activeLayoutHeight) { + if (activeLayoutHeight < 0) { + activeLayoutHeight = 0; + } + activeLayoutSize.setHeight(activeLayoutHeight); + + } + + private void setOuterLayoutWidth(int activeLayoutWidth) { + super.setWidth((activeLayoutWidth + activeMargins.getHorizontal()) + + "px"); + + } + + private void setOuterLayoutHeight(int activeLayoutHeight) { + super.setHeight((activeLayoutHeight + activeMargins.getVertical()) + + "px"); + + } + + /** + * Updates the spacing between components. Needs to be done only when + * components are added/removed. + */ + private void updateContainerMargins() { + ChildComponentContainer firstChildComponent = getFirstChildComponentContainer(); + if (firstChildComponent != null) { + firstChildComponent.setMarginLeft(0); + firstChildComponent.setMarginTop(0); + + for (ChildComponentContainer childComponent : widgetToComponentContainer + .values()) { + if (childComponent == firstChildComponent) { + continue; + } + + if (isHorizontal()) { + childComponent.setMarginLeft(activeSpacing.hSpacing); + } else { + childComponent.setMarginTop(activeSpacing.vSpacing); + } + } + } + } + + private boolean isHorizontal() { + return orientation == ORIENTATION_HORIZONTAL; + } + + private boolean isVertical() { + return orientation == ORIENTATION_VERTICAL; + } + + private ChildComponentContainer createChildContainer(Widget child) { + + // Create a container DIV for the child + ChildComponentContainer childComponent = new ChildComponentContainer( + child, orientation); + + return childComponent; + + } + + public RenderSpace getAllocatedSpace(Widget child) { + int width = 0; + int height = 0; + ChildComponentContainer childComponentContainer = getComponentContainer(child); + // WIDTH CALCULATION + if (isVertical()) { + width = activeLayoutSize.getWidth(); + width -= childComponentContainer.getCaptionWidthAfterComponent(); + } else if (!isDynamicWidth()) { + // HORIZONTAL + width = childComponentContainer.getContSize().getWidth(); + width -= childComponentContainer.getCaptionWidthAfterComponent(); + } + + // HEIGHT CALCULATION + if (isHorizontal()) { + height = activeLayoutSize.getHeight(); + height -= childComponentContainer.getCaptionHeightAboveComponent(); + } else if (!isDynamicHeight()) { + // VERTICAL + height = childComponentContainer.getContSize().getHeight(); + height -= childComponentContainer.getCaptionHeightAboveComponent(); + } + + // ApplicationConnection.getConsole().log( + // "allocatedSpace for " + Util.getSimpleName(child) + ": " + // + width + "," + height); + RenderSpace space = new RenderSpace(width, height); + return space; + } + + private void recalculateLayoutAndComponentSizes() { + recalculateLayout(); + + if (!(isDynamicHeight() && isDynamicWidth())) { + /* First update relative sized components */ + for (ChildComponentContainer componentContainer : widgetToComponentContainer + .values()) { + client.handleComponentRelativeSize(componentContainer + .getWidget()); + + // Update widget size from DOM + componentContainer.updateWidgetSize(); + } + } + + if (isDynamicHeight()) { + /* + * Height is not necessarily correct anymore as the height of + * components might have changed if the width has changed. + */ + + /* + * Get the new widget sizes from DOM and calculate new container + * sizes + */ + updateWidgetSizes(); + + /* Update layout dimensions based on widget sizes */ + recalculateLayout(); + } + + updateRelativeSizesInNonMainDirection(); + calculateAlignments(); + + setRootSize(); + } + + private void setRootSize() { + root.getStyle().setPropertyPx("width", activeLayoutSize.getWidth()); + root.getStyle().setPropertyPx("height", activeLayoutSize.getHeight()); + } + + public boolean requestLayout(Set children) { + for (Paintable p : children) { + /* Update widget size from DOM */ + ChildComponentContainer componentContainer = getComponentContainer((Widget) p); + // This should no longer be needed (after #2563) + // if (isDynamicWidth()) { + // componentContainer.setUnlimitedContainerWidth(); + // } else { + // componentContainer.setLimitedContainerWidth(activeLayoutSize + // .getWidth()); + // } + + componentContainer.updateWidgetSize(); + + /* + * If this is the result of an caption icon onload event the caption + * size may have changed + */ + componentContainer.updateCaptionSize(); + } + + Size sizeBefore = new Size(activeLayoutSize.getWidth(), + activeLayoutSize.getHeight()); + + recalculateLayoutAndComponentSizes(); + boolean sameSize = (sizeBefore.equals(activeLayoutSize)); + if (!sameSize) { + /* Must inform child components about possible size updates */ + client.runDescendentsLayout(this); + } + + /* Automatically propagated upwards if the size has changed */ + + return sameSize; + } + + @Override + public void setHeight(String height) { + Size sizeBefore = new Size(activeLayoutSize.getWidth(), + activeLayoutSize.getHeight()); + + super.setHeight(height); + + if (height != null && !height.equals("")) { + setActiveLayoutHeight(getOffsetHeight() + - activeMargins.getVertical()); + } + + if (isRendering) { + sizeHasChangedDuringRendering = true; + } else { + recalculateLayoutAndComponentSizes(); + boolean sameSize = (sizeBefore.equals(activeLayoutSize)); + if (!sameSize) { + /* Must inform child components about possible size updates */ + client.runDescendentsLayout(this); + } + } + } + + @Override + public void setWidth(String width) { + if (this.width.equals(width)) { + return; + } + Size sizeBefore = new Size(activeLayoutSize.getWidth(), + activeLayoutSize.getHeight()); + + super.setWidth(width); + this.width = width; + if (width != null && !width.equals("")) { + setActiveLayoutWidth(getOffsetWidth() + - activeMargins.getHorizontal()); + } + + if (isRendering) { + sizeHasChangedDuringRendering = true; + } else { + recalculateLayoutAndComponentSizes(); + boolean sameSize = (sizeBefore.equals(activeLayoutSize)); + if (!sameSize) { + /* Must inform child components about possible size updates */ + client.runDescendentsLayout(this); + } + /* + * If the height changes as a consequence of this we must inform the + * parent also + */ + if (isDynamicHeight() + && sizeBefore.getHeight() != activeLayoutSize.getHeight()) { + Util.notifyParentOfSizeChange(this, false); + } + + } + } + + protected void updateAlignmentsAndExpandRatios(UIDL uidl, + ArrayList renderedWidgets) { + + /* + * UIDL contains component alignments as a comma separated list. + * + * See com.vaadin.terminal.gwt.client.ui.AlignmentInfo.java for + * possible values. + */ + final int[] alignments = uidl.getIntArrayAttribute("alignments"); + + /* + * UIDL contains normalized expand ratios as a comma separated list. + */ + final int[] expandRatios = uidl.getIntArrayAttribute("expandRatios"); + + for (int i = 0; i < renderedWidgets.size(); i++) { + Widget widget = renderedWidgets.get(i); + + ChildComponentContainer container = getComponentContainer(widget); + + // Calculate alignment info + container.setAlignment(new AlignmentInfo(alignments[i])); + + // Update expand ratio + container.setExpandRatio(expandRatios[i]); + } + } + + public void updateCaption(Paintable component, UIDL uidl) { + ChildComponentContainer componentContainer = getComponentContainer((Widget) component); + componentContainer.updateCaption(uidl, client); + if (!isRendering) { + /* + * This was a component-only update and the possible size change + * must be propagated to the layout + */ + client.captionSizeUpdated(component); + } + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VPanel.java b/src/com/vaadin/terminal/gwt/client/ui/VPanel.java new file mode 100644 index 0000000000..e146eb7af5 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VPanel.java @@ -0,0 +1,516 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.Set; + +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.Document; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.Container; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.RenderInformation; +import com.vaadin.terminal.gwt.client.RenderSpace; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +public class VPanel extends SimplePanel implements Container { + + public static final String CLASSNAME = "i-panel"; + + ApplicationConnection client; + + String id; + + private final Element captionNode = DOM.createDiv(); + + private final Element captionText = DOM.createSpan(); + + private Icon icon; + + private final Element bottomDecoration = DOM.createDiv(); + + private final Element contentNode = DOM.createDiv(); + + private Element errorIndicatorElement; + + private String height; + + private Paintable layout; + + ShortcutActionHandler shortcutHandler; + + private String width = ""; + + private Element geckoCaptionMeter; + + private int scrollTop; + + private int scrollLeft; + + private RenderInformation renderInformation = new RenderInformation(); + + private int borderPaddingHorizontal = -1; + + private int borderPaddingVertical = -1; + + private int captionPaddingHorizontal = -1; + + private int captionMarginLeft = -1; + + private boolean rendering; + + private int contentMarginLeft = -1; + + private String previousStyleName; + + public VPanel() { + super(); + DivElement captionWrap = Document.get().createDivElement(); + captionWrap.appendChild(captionNode); + captionNode.appendChild(captionText); + + captionWrap.setClassName(CLASSNAME + "-captionwrap"); + captionNode.setClassName(CLASSNAME + "-caption"); + contentNode.setClassName(CLASSNAME + "-content"); + bottomDecoration.setClassName(CLASSNAME + "-deco"); + + getElement().appendChild(captionWrap); + getElement().appendChild(contentNode); + getElement().appendChild(bottomDecoration); + setStyleName(CLASSNAME); + DOM.sinkEvents(getElement(), Event.ONKEYDOWN); + DOM.sinkEvents(contentNode, Event.ONSCROLL); + contentNode.getStyle().setProperty("position", "relative"); + getElement().getStyle().setProperty("overflow", "hidden"); + } + + @Override + protected Element getContainerElement() { + return contentNode; + } + + private void setCaption(String text) { + DOM.setInnerHTML(captionText, text); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + rendering = true; + if (!uidl.hasAttribute("cached")) { + // Handle caption displaying and style names, prior generics. + // Affects size + // calculations + + // Restore default stylenames + contentNode.setClassName(CLASSNAME + "-content"); + bottomDecoration.setClassName(CLASSNAME + "-deco"); + captionNode.setClassName(CLASSNAME + "-caption"); + boolean hasCaption = false; + if (uidl.hasAttribute("caption") + && !uidl.getStringAttribute("caption").equals("")) { + setCaption(uidl.getStringAttribute("caption")); + hasCaption = true; + } else { + setCaption(""); + captionNode.setClassName(CLASSNAME + "-nocaption"); + } + + // Add proper stylenames for all elements. This way we can prevent + // unwanted CSS selector inheritance. + if (uidl.hasAttribute("style")) { + final String[] styles = uidl.getStringAttribute("style").split( + " "); + final String captionBaseClass = CLASSNAME + + (hasCaption ? "-caption" : "-nocaption"); + final String contentBaseClass = CLASSNAME + "-content"; + final String decoBaseClass = CLASSNAME + "-deco"; + String captionClass = captionBaseClass; + String contentClass = contentBaseClass; + String decoClass = decoBaseClass; + for (int i = 0; i < styles.length; i++) { + captionClass += " " + captionBaseClass + "-" + styles[i]; + contentClass += " " + contentBaseClass + "-" + styles[i]; + decoClass += " " + decoBaseClass + "-" + styles[i]; + } + captionNode.setClassName(captionClass); + contentNode.setClassName(contentClass); + bottomDecoration.setClassName(decoClass); + + } + } + // Ensure correct implementation + if (client.updateComponent(this, uidl, false)) { + rendering = false; + return; + } + + this.client = client; + id = uidl.getId(); + + setIconUri(uidl, client); + + handleError(uidl); + + // Render content + final UIDL layoutUidl = uidl.getChildUIDL(0); + final Paintable newLayout = client.getPaintable(layoutUidl); + if (newLayout != layout) { + if (layout != null) { + client.unregisterPaintable(layout); + } + setWidget((Widget) newLayout); + layout = newLayout; + } + layout.updateFromUIDL(layoutUidl, client); + + runHacks(false); + // We may have actions attached to this panel + if (uidl.getChildCount() > 1) { + final int cnt = uidl.getChildCount(); + for (int i = 1; i < cnt; i++) { + UIDL childUidl = uidl.getChildUIDL(i); + if (childUidl.getTag().equals("actions")) { + if (shortcutHandler == null) { + shortcutHandler = new ShortcutActionHandler(id, client); + } + shortcutHandler.updateActionMap(childUidl); + } + } + } + + if (uidl.hasVariable("scrollTop") + && uidl.getIntVariable("scrollTop") != scrollTop) { + scrollTop = uidl.getIntVariable("scrollTop"); + DOM.setElementPropertyInt(contentNode, "scrollTop", scrollTop); + } + + if (uidl.hasVariable("scrollLeft") + && uidl.getIntVariable("scrollLeft") != scrollLeft) { + scrollLeft = uidl.getIntVariable("scrollLeft"); + DOM.setElementPropertyInt(contentNode, "scrollLeft", scrollLeft); + } + + rendering = false; + + } + + @Override + public void setStyleName(String style) { + if (!style.equals(previousStyleName)) { + super.setStyleName(style); + detectContainerBorders(); + previousStyleName = style; + } + } + + private void handleError(UIDL uidl) { + if (uidl.hasAttribute("error")) { + if (errorIndicatorElement == null) { + errorIndicatorElement = DOM.createDiv(); + DOM.setElementProperty(errorIndicatorElement, "className", + "i-errorindicator"); + DOM.sinkEvents(errorIndicatorElement, Event.MOUSEEVENTS); + sinkEvents(Event.MOUSEEVENTS); + } + DOM.insertBefore(captionNode, errorIndicatorElement, captionText); + } else if (errorIndicatorElement != null) { + DOM.removeChild(captionNode, errorIndicatorElement); + errorIndicatorElement = null; + } + } + + private void setIconUri(UIDL uidl, ApplicationConnection client) { + final String iconUri = uidl.hasAttribute("icon") ? uidl + .getStringAttribute("icon") : null; + if (iconUri == null) { + if (icon != null) { + DOM.removeChild(captionNode, icon.getElement()); + icon = null; + } + } else { + if (icon == null) { + icon = new Icon(client); + DOM.insertChild(captionNode, icon.getElement(), 0); + } + icon.setUri(iconUri); + } + } + + public void runHacks(boolean runGeckoFix) { + if (BrowserInfo.get().isIE6() && width != null && !width.equals("")) { + /* + * IE6 requires overflow-hidden elements to have a width specified + * so we calculate the width of the content and caption nodes when + * no width has been specified. + */ + /* + * Fixes #1923 VPanel: Horizontal scrollbar does not appear in IE6 + * with wide content + */ + + /* + * Caption must be shrunk for parent measurements to return correct + * result in IE6 + */ + DOM.setStyleAttribute(captionNode, "width", "1px"); + + int parentPadding = Util.measureHorizontalPaddingAndBorder( + getElement(), 0); + + int parentWidthExcludingPadding = getElement().getOffsetWidth() + - parentPadding; + + Util.setWidthExcludingPaddingAndBorder(captionNode, + parentWidthExcludingPadding - getCaptionMarginLeft(), 26, + false); + + int contentMarginLeft = getContentMarginLeft(); + + Util.setWidthExcludingPaddingAndBorder(contentNode, + parentWidthExcludingPadding - contentMarginLeft, 2, false); + + } + + if ((BrowserInfo.get().isIE() || BrowserInfo.get().isFF2()) + && (width == null || width.equals(""))) { + /* + * IE and FF2 needs width to be specified for the root DIV so we + * calculate that from the sizes of the caption and layout + */ + int captionWidth = captionText.getOffsetWidth() + + getCaptionMarginLeft() + getCaptionPaddingHorizontal(); + int layoutWidth = ((Widget) layout).getOffsetWidth() + + getContainerBorderWidth(); + int width = layoutWidth; + if (captionWidth > width) { + width = captionWidth; + } + + if (BrowserInfo.get().isIE7()) { + Util.setWidthExcludingPaddingAndBorder(captionNode, width + - getCaptionMarginLeft(), 26, false); + } + + super.setWidth(width + "px"); + } + + if (runGeckoFix && BrowserInfo.get().isGecko()) { + // workaround for #1764 + if (width == null || width.equals("")) { + if (geckoCaptionMeter == null) { + geckoCaptionMeter = DOM.createDiv(); + DOM.appendChild(captionNode, geckoCaptionMeter); + } + int captionWidth = DOM.getElementPropertyInt(captionText, + "offsetWidth"); + int availWidth = DOM.getElementPropertyInt(geckoCaptionMeter, + "offsetWidth"); + if (captionWidth == availWidth) { + /* + * Caption width defines panel width -> Gecko based browsers + * somehow fails to float things right, without the + * "noncode" below + */ + setWidth(getOffsetWidth() + "px"); + } else { + DOM.setStyleAttribute(captionNode, "width", ""); + } + } + } + + client.runDescendentsLayout(this); + + Util.runWebkitOverflowAutoFix(contentNode); + + } + + @Override + public void onBrowserEvent(Event event) { + final Element target = DOM.eventGetTarget(event); + final int type = DOM.eventGetType(event); + if (type == Event.ONKEYDOWN && shortcutHandler != null) { + shortcutHandler.handleKeyboardEvent(event); + return; + } + if (type == Event.ONSCROLL) { + int newscrollTop = DOM.getElementPropertyInt(contentNode, + "scrollTop"); + int newscrollLeft = DOM.getElementPropertyInt(contentNode, + "scrollLeft"); + if (client != null + && (newscrollLeft != scrollLeft || newscrollTop != scrollTop)) { + scrollLeft = newscrollLeft; + scrollTop = newscrollTop; + client.updateVariable(id, "scrollTop", scrollTop, false); + client.updateVariable(id, "scrollLeft", scrollLeft, false); + } + } else if (captionNode.isOrHasChild(target)) { + if (client != null) { + client.handleTooltipEvent(event, this); + } + } + } + + @Override + public void setHeight(String height) { + this.height = height; + super.setHeight(height); + if (height != null && height != "") { + final int targetHeight = getOffsetHeight(); + int containerHeight = targetHeight - captionNode.getOffsetHeight() + - bottomDecoration.getOffsetHeight() + - getContainerBorderHeight(); + if (containerHeight < 0) { + containerHeight = 0; + } + DOM + .setStyleAttribute(contentNode, "height", containerHeight + + "px"); + } else { + DOM.setStyleAttribute(contentNode, "height", ""); + } + if (!rendering) { + runHacks(true); + } + } + + private int getCaptionMarginLeft() { + if (captionMarginLeft < 0) { + detectContainerBorders(); + } + return captionMarginLeft; + } + + private int getContentMarginLeft() { + if (contentMarginLeft < 0) { + detectContainerBorders(); + } + return contentMarginLeft; + } + + private int getCaptionPaddingHorizontal() { + if (captionPaddingHorizontal < 0) { + detectContainerBorders(); + } + return captionPaddingHorizontal; + } + + private int getContainerBorderHeight() { + if (borderPaddingVertical < 0) { + detectContainerBorders(); + } + return borderPaddingVertical; + } + + @Override + public void setWidth(String width) { + if (this.width.equals(width)) { + return; + } + + this.width = width; + super.setWidth(width); + if (!rendering) { + runHacks(true); + + if (height.equals("")) { + // Width change may affect height + Util.updateRelativeChildrenAndSendSizeUpdateEvent(client, this); + } + + } + } + + private int getContainerBorderWidth() { + if (borderPaddingHorizontal < 0) { + detectContainerBorders(); + } + return borderPaddingHorizontal; + } + + private void detectContainerBorders() { + DOM.setStyleAttribute(contentNode, "overflow", "hidden"); + + borderPaddingHorizontal = Util.measureHorizontalBorder(contentNode); + borderPaddingVertical = Util.measureVerticalBorder(contentNode); + + DOM.setStyleAttribute(contentNode, "overflow", "auto"); + + captionPaddingHorizontal = Util.measureHorizontalPaddingAndBorder( + captionNode, 26); + + captionMarginLeft = Util.measureMarginLeft(captionNode); + contentMarginLeft = Util.measureMarginLeft(contentNode); + + } + + public boolean hasChildComponent(Widget component) { + if (component != null && component == layout) { + return true; + } else { + return false; + } + } + + public void replaceChildComponent(Widget oldComponent, Widget newComponent) { + // TODO This is untested as no layouts require this + if (oldComponent != layout) { + return; + } + + setWidget(newComponent); + layout = (Paintable) newComponent; + } + + public RenderSpace getAllocatedSpace(Widget child) { + int w = 0; + int h = 0; + + if (width != null && !width.equals("")) { + w = getOffsetWidth() - getContainerBorderWidth(); + if (w < 0) { + w = 0; + } + } + + if (height != null && !height.equals("")) { + h = contentNode.getOffsetHeight() - getContainerBorderHeight(); + if (h < 0) { + h = 0; + } + } + + return new RenderSpace(w, h, true); + } + + public boolean requestLayout(Set child) { + if (height != null && height != "" && width != null && width != "") { + /* + * If the height and width has been specified the child components + * cannot make the size of the layout change + */ + return true; + } + runHacks(false); + return !renderInformation.updateSize(getElement()); + } + + public void updateCaption(Paintable component, UIDL uidl) { + // NOP: layouts caption, errors etc not rendered in Panel + } + + @Override + protected void onAttach() { + super.onAttach(); + detectContainerBorders(); + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VPasswordField.java b/src/com/vaadin/terminal/gwt/client/ui/VPasswordField.java new file mode 100644 index 0000000000..db863e4b70 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VPasswordField.java @@ -0,0 +1,21 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.user.client.DOM; + +/** + * This class represents a password field. + * + * @author IT Mill Ltd. + * + */ +public class VPasswordField extends VTextField { + + public VPasswordField() { + super(DOM.createInputPassword()); + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VPopupCalendar.java b/src/com/vaadin/terminal/gwt/client/ui/VPopupCalendar.java new file mode 100644 index 0000000000..31004be71d --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VPopupCalendar.java @@ -0,0 +1,130 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.ClickListener; +import com.google.gwt.user.client.ui.PopupListener; +import com.google.gwt.user.client.ui.PopupPanel; +import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.user.client.ui.PopupPanel.PositionCallback; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; + +public class VPopupCalendar extends VTextualDate implements Paintable, Field, + ClickListener, PopupListener { + + private final Button calendarToggle; + + private final VCalendarPanel calendar; + + private final VToolkitOverlay popup; + private boolean open = false; + + public VPopupCalendar() { + super(); + + calendarToggle = new Button(); + calendarToggle.setStyleName(CLASSNAME + "-button"); + calendarToggle.setText(""); + calendarToggle.addClickListener(this); + add(calendarToggle); + + calendar = new VCalendarPanel(this); + popup = new VToolkitOverlay(true, true, true); + popup.setStyleName(VDateField.CLASSNAME + "-popup"); + popup.setWidget(calendar); + popup.addPopupListener(this); + + DOM.setElementProperty(calendar.getElement(), "id", + "PID_TOOLKIT_POPUPCAL"); + + } + + @Override + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + super.updateFromUIDL(uidl, client); + if (date != null) { + calendar.updateCalendar(); + } + calendarToggle.setEnabled(enabled); + } + + public void onClick(Widget sender) { + if (sender == calendarToggle && !open) { + open = true; + calendar.updateCalendar(); + // clear previous values + popup.setWidth(""); + popup.setHeight(""); + popup.setPopupPositionAndShow(new PositionCallback() { + public void setPosition(int offsetWidth, int offsetHeight) { + final int w = offsetWidth; + final int h = offsetHeight; + int t = calendarToggle.getAbsoluteTop(); + int l = calendarToggle.getAbsoluteLeft(); + if (l + w > Window.getClientWidth() + + Window.getScrollLeft()) { + l = Window.getClientWidth() + Window.getScrollLeft() + - w; + } + if (t + h + calendarToggle.getOffsetHeight() + 30 > Window + .getClientHeight() + + Window.getScrollTop()) { + t = Window.getClientHeight() + Window.getScrollTop() + - h - calendarToggle.getOffsetHeight() - 30; + l += calendarToggle.getOffsetWidth(); + } + + // fix size + popup.setWidth(w + "px"); + popup.setHeight(h + "px"); + + popup.setPopupPosition(l, t + + calendarToggle.getOffsetHeight() + 2); + + setFocus(true); + } + }); + } + } + + public void onPopupClosed(PopupPanel sender, boolean autoClosed) { + if (sender == popup) { + buildDate(); + // Sigh. + Timer t = new Timer() { + @Override + public void run() { + open = false; + } + }; + t.schedule(100); + } + } + + /** + * Sets focus to Calendar panel. + * + * @param focus + */ + public void setFocus(boolean focus) { + calendar.setFocus(focus); + } + + @Override + protected int getFieldExtraWidth() { + if (fieldExtraWidth < 0) { + fieldExtraWidth = super.getFieldExtraWidth(); + fieldExtraWidth += calendarToggle.getOffsetWidth(); + } + return fieldExtraWidth; + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VPopupView.java b/src/com/vaadin/terminal/gwt/client/ui/VPopupView.java new file mode 100644 index 0000000000..c89066578a --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VPopupView.java @@ -0,0 +1,417 @@ +package com.vaadin.terminal.gwt.client.ui; + +import java.util.HashSet; +import java.util.Set; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.ClickListener; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.HasFocus; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.PopupListener; +import com.google.gwt.user.client.ui.PopupPanel; +import com.google.gwt.user.client.ui.RootPanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.Container; +import com.vaadin.terminal.gwt.client.VCaption; +import com.vaadin.terminal.gwt.client.VCaptionWrapper; +import com.vaadin.terminal.gwt.client.VTooltip; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.RenderSpace; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; +import com.vaadin.terminal.gwt.client.RenderInformation.Size; + +public class VPopupView extends HTML implements Container { + + public static final String CLASSNAME = "i-popupview"; + + /** For server-client communication */ + private String uidlId; + private ApplicationConnection client; + + /** This variable helps to communicate popup visibility to the server */ + private boolean hostPopupVisible; + + private final CustomPopup popup; + private final Label loading = new Label("Loading..."); + + /** + * loading constructor + */ + public VPopupView() { + super(); + popup = new CustomPopup(); + + setStyleName(CLASSNAME); + popup.setStylePrimaryName(CLASSNAME + "-popup"); + + setHTML("(No HTML defined for PopupView)"); + popup.setWidget(loading); + + // When we click to open the popup... + addClickListener(new ClickListener() { + public void onClick(Widget sender) { + updateState(true); + } + }); + + // ..and when we close it + popup.addPopupListener(new PopupListener() { + public void onPopupClosed(PopupPanel sender, boolean autoClosed) { + updateState(false); + } + }); + + popup.setAnimationEnabled(true); + sinkEvents(VTooltip.TOOLTIP_EVENTS); + } + + /** + * + * + * @see com.vaadin.terminal.gwt.client.Paintable#updateFromUIDL(com.vaadin.terminal.gwt.client.UIDL, + * com.vaadin.terminal.gwt.client.ApplicationConnection) + */ + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + // This call should be made first. Ensure correct implementation, + // and don't let the containing layout manage caption. + if (client.updateComponent(this, uidl, false)) { + return; + } + // These are for future server connections + this.client = client; + uidlId = uidl.getId(); + + hostPopupVisible = uidl.getBooleanVariable("popupVisibility"); + + setHTML(uidl.getStringAttribute("html")); + + if (uidl.hasAttribute("hideOnMouseOut")) { + popup.setHideOnMouseOut(uidl.getBooleanAttribute("hideOnMouseOut")); + } + + // Render the popup if visible and show it. + if (hostPopupVisible) { + UIDL popupUIDL = uidl.getChildUIDL(0); + + // showPopupOnTop(popup, hostReference); + preparePopup(popup); + popup.updateFromUIDL(popupUIDL, client); + if (uidl.hasAttribute("style")) { + final String[] styles = uidl.getStringAttribute("style").split( + " "); + final StringBuffer styleBuf = new StringBuffer(); + final String primaryName = popup.getStylePrimaryName(); + styleBuf.append(primaryName); + for (int i = 0; i < styles.length; i++) { + styleBuf.append(" "); + styleBuf.append(primaryName); + styleBuf.append("-"); + styleBuf.append(styles[i]); + } + popup.setStyleName(styleBuf.toString()); + } else { + popup.setStyleName(popup.getStylePrimaryName()); + } + showPopup(popup); + + // The popup shouldn't be visible, try to hide it. + } else { + popup.hide(); + } + }// updateFromUIDL + + /** + * Update popup visibility to server + * + * @param visibility + */ + private void updateState(boolean visible) { + // If we know the server connection + // then update the current situation + if (uidlId != null && client != null && isAttached()) { + client.updateVariable(uidlId, "popupVisibility", visible, true); + } + } + + private void preparePopup(final CustomPopup popup) { + popup.setVisible(false); + popup.show(); + } + + private void showPopup(final CustomPopup popup) { + int windowTop = RootPanel.get().getAbsoluteTop(); + int windowLeft = RootPanel.get().getAbsoluteLeft(); + int windowRight = windowLeft + RootPanel.get().getOffsetWidth(); + int windowBottom = windowTop + RootPanel.get().getOffsetHeight(); + + int offsetWidth = popup.getOffsetWidth(); + int offsetHeight = popup.getOffsetHeight(); + + int hostHorizontalCenter = VPopupView.this.getAbsoluteLeft() + + VPopupView.this.getOffsetWidth() / 2; + int hostVerticalCenter = VPopupView.this.getAbsoluteTop() + + VPopupView.this.getOffsetHeight() / 2; + + int left = hostHorizontalCenter - offsetWidth / 2; + int top = hostVerticalCenter - offsetHeight / 2; + + // Superclass takes care of top and left + if ((left + offsetWidth) > windowRight) { + left -= (left + offsetWidth) - windowRight; + } + + if ((top + offsetHeight) > windowBottom) { + top -= (top + offsetHeight) - windowBottom; + } + + popup.setPopupPosition(left, top); + + popup.setVisible(true); + } + + /** + * Make sure that we remove the popup when the main widget is removed. + * + * @see com.google.gwt.user.client.ui.Widget#onUnload() + */ + @Override + protected void onDetach() { + popup.hide(); + super.onDetach(); + } + + private static native void nativeBlur(Element e) + /*-{ + if(e && e.blur) { + e.blur(); + } + }-*/; + + private class CustomPopup extends VToolkitOverlay { + + private Paintable popupComponentPaintable = null; + private Widget popupComponentWidget = null; + private VCaptionWrapper captionWrapper = null; + + private boolean hasHadMouseOver = false; + private boolean hideOnMouseOut = true; + private final Set activeChildren = new HashSet(); + private boolean hiding = false; + + public CustomPopup() { + super(true, false, true); // autoHide, not modal, dropshadow + } + + // For some reason ONMOUSEOUT events are not always received, so we have + // to use ONMOUSEMOVE that doesn't target the popup + @Override + public boolean onEventPreview(Event event) { + Element target = DOM.eventGetTarget(event); + boolean eventTargetsPopup = DOM.isOrHasChild(getElement(), target); + int type = DOM.eventGetType(event); + + // Catch children that use keyboard, so we can unfocus them when + // hiding + if (eventTargetsPopup && type == Event.ONKEYPRESS) { + activeChildren.add(target); + } + + if (eventTargetsPopup && type == Event.ONMOUSEMOVE) { + hasHadMouseOver = true; + } + + if (!eventTargetsPopup && type == Event.ONMOUSEMOVE) { + + if (hasHadMouseOver && hideOnMouseOut) { + hide(); + return true; + } + } + + return super.onEventPreview(event); + } + + @Override + public void hide(boolean autoClosed) { + hiding = true; + syncChildren(); + unregisterPaintables(); + if (popupComponentWidget != null && popupComponentWidget != loading) { + remove(popupComponentWidget); + } + hasHadMouseOver = false; + super.hide(autoClosed); + } + + @Override + public void show() { + hiding = false; + super.show(); + } + + /** + * Try to sync all known active child widgets to server + */ + public void syncChildren() { + // Notify children with focus + if ((popupComponentWidget instanceof HasFocus)) { + ((HasFocus) popupComponentWidget).setFocus(false); + } + + // Notify children that have used the keyboard + for (Element e : activeChildren) { + try { + nativeBlur(e); + } catch (Exception ignored) { + } + } + activeChildren.clear(); + } + + @Override + public boolean remove(Widget w) { + + popupComponentPaintable = null; + popupComponentWidget = null; + captionWrapper = null; + + return super.remove(w); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + + Paintable newPopupComponent = client.getPaintable(uidl + .getChildUIDL(0)); + + if (newPopupComponent != popupComponentPaintable) { + + setWidget((Widget) newPopupComponent); + + popupComponentWidget = (Widget) newPopupComponent; + + popupComponentPaintable = newPopupComponent; + } + + popupComponentPaintable + .updateFromUIDL(uidl.getChildUIDL(0), client); + + } + + public void unregisterPaintables() { + if (popupComponentPaintable != null) { + client.unregisterPaintable(popupComponentPaintable); + } + } + + public void setHideOnMouseOut(boolean hideOnMouseOut) { + this.hideOnMouseOut = hideOnMouseOut; + } + + /* + * + * We need a hack make popup act as a child of VPopupView in toolkits + * component tree, but work in default GWT manner when closing or + * opening. + * + * (non-Javadoc) + * + * @see com.google.gwt.user.client.ui.Widget#getParent() + */ + @Override + public Widget getParent() { + if (!isAttached() || hiding) { + return super.getParent(); + } else { + return VPopupView.this; + } + } + + @Override + protected void onDetach() { + super.onDetach(); + hiding = false; + } + + @Override + public Element getContainerElement() { + return super.getContainerElement(); + } + + }// class CustomPopup + + // Container methods + + public RenderSpace getAllocatedSpace(Widget child) { + Size popupExtra = calculatePopupExtra(); + + return new RenderSpace(RootPanel.get().getOffsetWidth() + - popupExtra.getWidth(), RootPanel.get().getOffsetHeight() + - popupExtra.getHeight()); + } + + /** + * Calculate extra space taken by the popup decorations + * + * @return + */ + protected Size calculatePopupExtra() { + Element pe = popup.getElement(); + Element ipe = popup.getContainerElement(); + + // border + padding + int width = Util.getRequiredWidth(pe) - Util.getRequiredWidth(ipe); + int height = Util.getRequiredHeight(pe) - Util.getRequiredHeight(ipe); + + return new Size(width, height); + } + + public boolean hasChildComponent(Widget component) { + if (popup.popupComponentWidget != null) { + return popup.popupComponentWidget == component; + } else { + return false; + } + } + + public void replaceChildComponent(Widget oldComponent, Widget newComponent) { + popup.setWidget(newComponent); + popup.popupComponentWidget = newComponent; + } + + public boolean requestLayout(Set child) { + return true; + } + + public void updateCaption(Paintable component, UIDL uidl) { + if (VCaption.isNeeded(uidl)) { + if (popup.captionWrapper != null) { + popup.captionWrapper.updateCaption(uidl); + } else { + popup.captionWrapper = new VCaptionWrapper(component, client); + popup.setWidget(popup.captionWrapper); + popup.captionWrapper.updateCaption(uidl); + } + } else { + if (popup.captionWrapper != null) { + popup.setWidget(popup.popupComponentWidget); + } + } + + popup.popupComponentWidget = (Widget) component; + popup.popupComponentPaintable = component; + } + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + if (client != null) { + client.handleTooltipEvent(event, this); + } + } + +}// class VPopupView diff --git a/src/com/vaadin/terminal/gwt/client/ui/VProgressIndicator.java b/src/com/vaadin/terminal/gwt/client/ui/VProgressIndicator.java new file mode 100644 index 0000000000..5da78be59f --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VProgressIndicator.java @@ -0,0 +1,100 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; + +public class VProgressIndicator extends Widget implements Paintable { + + private static final String CLASSNAME = "i-progressindicator"; + Element wrapper = DOM.createDiv(); + Element indicator = DOM.createDiv(); + private ApplicationConnection client; + private final Poller poller; + private boolean indeterminate = false; + private boolean pollerSuspendedDueDetach; + + public VProgressIndicator() { + setElement(DOM.createDiv()); + getElement().appendChild(wrapper); + setStyleName(CLASSNAME); + wrapper.appendChild(indicator); + indicator.setClassName(CLASSNAME + "-indicator"); + wrapper.setClassName(CLASSNAME + "-wrapper"); + poller = new Poller(); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + if (client.updateComponent(this, uidl, true)) { + return; + } + + poller.cancel(); + this.client = client; + if (client.updateComponent(this, uidl, true)) { + return; + } + + indeterminate = uidl.getBooleanAttribute("indeterminate"); + + if (indeterminate) { + String basename = CLASSNAME + "-indeterminate"; + VProgressIndicator.setStyleName(getElement(), basename, true); + VProgressIndicator.setStyleName(getElement(), basename + + "-disabled", uidl.getBooleanAttribute("disabled")); + } else { + try { + final float f = Float.parseFloat(uidl + .getStringAttribute("state")); + final int size = Math.round(100 * f); + DOM.setStyleAttribute(indicator, "width", size + "%"); + } catch (final Exception e) { + } + } + + if (!uidl.getBooleanAttribute("disabled")) { + poller.scheduleRepeating(uidl.getIntAttribute("pollinginterval")); + } + } + + @Override + protected void onAttach() { + super.onAttach(); + if (pollerSuspendedDueDetach) { + poller.run(); + } + } + + @Override + protected void onDetach() { + super.onDetach(); + poller.cancel(); + pollerSuspendedDueDetach = true; + } + + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + if (!visible) { + poller.cancel(); + } + } + + class Poller extends Timer { + + @Override + public void run() { + client.sendPendingVariableChanges(); + } + + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java b/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java new file mode 100644 index 0000000000..a1efd5d75c --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java @@ -0,0 +1,2841 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.Vector; + +import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.NodeList; +import com.google.gwt.dom.client.TableCellElement; +import com.google.gwt.dom.client.TableRowElement; +import com.google.gwt.dom.client.TableSectionElement; +import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.DeferredCommand; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.Panel; +import com.google.gwt.user.client.ui.RootPanel; +import com.google.gwt.user.client.ui.ScrollListener; +import com.google.gwt.user.client.ui.ScrollPanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.Container; +import com.vaadin.terminal.gwt.client.MouseEventDetails; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.RenderSpace; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; +import com.vaadin.terminal.gwt.client.ui.VScrollTable.VScrollTableBody.VScrollTableRow; + +/** + * VScrollTable + * + * VScrollTable is a FlowPanel having two widgets in it: * TableHead component * + * ScrollPanel + * + * TableHead contains table's header and widgets + logic for resizing, + * reordering and hiding columns. + * + * ScrollPanel contains VScrollTableBody object which handles content. To save + * some bandwidth and to improve clients responsiveness with loads of data, in + * VScrollTableBody all rows are not necessary rendered. There are "spacers" in + * VScrollTableBody to use the exact same space as non-rendered rows would use. + * This way we can use seamlessly traditional scrollbars and scrolling to fetch + * more rows instead of "paging". + * + * In VScrollTable we listen to scroll events. On horizontal scrolling we also + * update TableHeads scroll position which has its scrollbars hidden. On + * vertical scroll events we will check if we are reaching the end of area where + * we have rows rendered and + * + * TODO implement unregistering for child components in Cells + */ +public class VScrollTable extends FlowPanel implements Table, ScrollListener { + + public static final String CLASSNAME = "i-table"; + /** + * multiple of pagelength which component will cache when requesting more + * rows + */ + private static final double CACHE_RATE = 2; + /** + * fraction of pageLenght which can be scrolled without making new request + */ + private static final double CACHE_REACT_RATE = 1.5; + + public static final char ALIGN_CENTER = 'c'; + public static final char ALIGN_LEFT = 'b'; + public static final char ALIGN_RIGHT = 'e'; + private int firstRowInViewPort = 0; + private int pageLength = 15; + private int lastRequestedFirstvisible = 0; // to detect "serverside scroll" + + private boolean showRowHeaders = false; + + private String[] columnOrder; + + private ApplicationConnection client; + private String paintableId; + + private boolean immediate; + + private int selectMode = Table.SELECT_MODE_NONE; + + private final HashSet selectedRowKeys = new HashSet(); + + private boolean initializedAndAttached = false; + + /** + * Flag to indicate if a column width recalculation is needed due update. + */ + private boolean headerChangedDuringUpdate = false; + + private final TableHead tHead = new TableHead(); + + private final ScrollPanel bodyContainer = new ScrollPanel(); + + private int totalRows; + + private Set collapsedColumns; + + private final RowRequestHandler rowRequestHandler; + private VScrollTableBody tBody; + private int firstvisible = 0; + private boolean sortAscending; + private String sortColumn; + private boolean columnReordering; + + /** + * This map contains captions and icon urls for actions like: * "33_c" -> + * "Edit" * "33_i" -> "http://dom.com/edit.png" + */ + private final HashMap actionMap = new HashMap(); + private String[] visibleColOrder; + private boolean initialContentReceived = false; + private Element scrollPositionElement; + private boolean enabled; + private boolean showColHeaders; + + /** flag to indicate that table body has changed */ + private boolean isNewBody = true; + + private boolean emitClickEvents; + + /* + * Read from the "recalcWidths" -attribute. When it is true, the table will + * recalculate the widths for columns - desirable in some cases. For #1983, + * marked experimental. + */ + boolean recalcWidths = false; + + private final ArrayList lazyUnregistryBag = new ArrayList(); + private String height; + private String width = ""; + private boolean rendering = false; + + public VScrollTable() { + bodyContainer.addScrollListener(this); + bodyContainer.setStyleName(CLASSNAME + "-body"); + + setStyleName(CLASSNAME); + add(tHead); + add(bodyContainer); + + rowRequestHandler = new RowRequestHandler(); + + } + + @SuppressWarnings("unchecked") + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + rendering = true; + if (client.updateComponent(this, uidl, true)) { + rendering = false; + return; + } + + // we may have pending cache row fetch, cancel it. See #2136 + rowRequestHandler.cancel(); + + enabled = !uidl.hasAttribute("disabled"); + + this.client = client; + paintableId = uidl.getStringAttribute("id"); + immediate = uidl.getBooleanAttribute("immediate"); + emitClickEvents = uidl.getBooleanAttribute("listenClicks"); + final int newTotalRows = uidl.getIntAttribute("totalrows"); + if (newTotalRows != totalRows) { + if (tBody != null) { + if (totalRows == 0) { + tHead.clear(); + } + initializedAndAttached = false; + initialContentReceived = false; + isNewBody = true; + } + totalRows = newTotalRows; + } + + recalcWidths = uidl.hasAttribute("recalcWidths"); + + pageLength = uidl.getIntAttribute("pagelength"); + if (pageLength == 0) { + pageLength = totalRows; + } + firstvisible = uidl.hasVariable("firstvisible") ? uidl + .getIntVariable("firstvisible") : 0; + if (firstvisible != lastRequestedFirstvisible && tBody != null) { + // received 'surprising' firstvisible from server: scroll there + firstRowInViewPort = firstvisible; + bodyContainer + .setScrollPosition(firstvisible * tBody.getRowHeight()); + } + + showRowHeaders = uidl.getBooleanAttribute("rowheaders"); + showColHeaders = uidl.getBooleanAttribute("colheaders"); + + if (uidl.hasVariable("sortascending")) { + sortAscending = uidl.getBooleanVariable("sortascending"); + sortColumn = uidl.getStringVariable("sortcolumn"); + } + + if (uidl.hasVariable("selected")) { + final Set selectedKeys = uidl + .getStringArrayVariableAsSet("selected"); + selectedRowKeys.clear(); + for (String string : selectedKeys) { + selectedRowKeys.add(string); + } + } + + if (uidl.hasAttribute("selectmode")) { + if (uidl.getBooleanAttribute("readonly")) { + selectMode = Table.SELECT_MODE_NONE; + } else if (uidl.getStringAttribute("selectmode").equals("multi")) { + selectMode = Table.SELECT_MODE_MULTI; + } else if (uidl.getStringAttribute("selectmode").equals("single")) { + selectMode = Table.SELECT_MODE_SINGLE; + } else { + selectMode = Table.SELECT_MODE_NONE; + } + } + + if (uidl.hasVariable("columnorder")) { + columnReordering = true; + columnOrder = uidl.getStringArrayVariable("columnorder"); + } + + if (uidl.hasVariable("collapsedcolumns")) { + tHead.setColumnCollapsingAllowed(true); + collapsedColumns = uidl + .getStringArrayVariableAsSet("collapsedcolumns"); + } else { + tHead.setColumnCollapsingAllowed(false); + } + + UIDL rowData = null; + for (final Iterator it = uidl.getChildIterator(); it.hasNext();) { + final UIDL c = (UIDL) it.next(); + if (c.getTag().equals("rows")) { + rowData = c; + } else if (c.getTag().equals("actions")) { + updateActionMap(c); + } else if (c.getTag().equals("visiblecolumns")) { + tHead.updateCellsFromUIDL(c); + } + } + updateHeader(uidl.getStringArrayAttribute("vcolorder")); + + if (!recalcWidths && initializedAndAttached) { + updateBody(rowData, uidl.getIntAttribute("firstrow"), uidl + .getIntAttribute("rows")); + if (headerChangedDuringUpdate) { + lazyAdjustColumnWidths.schedule(1); + } + } else { + if (tBody != null) { + tBody.removeFromParent(); + lazyUnregistryBag.add(tBody); + } + tBody = new VScrollTableBody(); + + tBody.renderInitialRows(rowData, uidl.getIntAttribute("firstrow"), + uidl.getIntAttribute("rows")); + bodyContainer.add(tBody); + initialContentReceived = true; + if (isAttached()) { + sizeInit(); + } + } + hideScrollPositionAnnotation(); + purgeUnregistryBag(); + rendering = false; + headerChangedDuringUpdate = false; + } + + /** + * Unregisters Paintables in "trashed" HasWidgets (IScrollTableBodys or + * IScrollTableRows). This is done lazily as Table must survive from + * "subtreecaching" logic. + */ + private void purgeUnregistryBag() { + for (Iterator iterator = lazyUnregistryBag.iterator(); iterator + .hasNext();) { + client.unregisterChildPaintables(iterator.next()); + } + lazyUnregistryBag.clear(); + } + + private void updateActionMap(UIDL c) { + final Iterator it = c.getChildIterator(); + while (it.hasNext()) { + final UIDL action = (UIDL) it.next(); + final String key = action.getStringAttribute("key"); + final String caption = action.getStringAttribute("caption"); + actionMap.put(key + "_c", caption); + if (action.hasAttribute("icon")) { + // TODO need some uri handling ?? + actionMap.put(key + "_i", client.translateToolkitUri(action + .getStringAttribute("icon"))); + } + } + + } + + public String getActionCaption(String actionKey) { + return actionMap.get(actionKey + "_c"); + } + + public String getActionIcon(String actionKey) { + return actionMap.get(actionKey + "_i"); + } + + private void updateHeader(String[] strings) { + if (strings == null) { + return; + } + + int visibleCols = strings.length; + int colIndex = 0; + if (showRowHeaders) { + tHead.enableColumn("0", colIndex); + visibleCols++; + visibleColOrder = new String[visibleCols]; + visibleColOrder[colIndex] = "0"; + colIndex++; + } else { + visibleColOrder = new String[visibleCols]; + tHead.removeCell("0"); + } + + int i; + for (i = 0; i < strings.length; i++) { + final String cid = strings[i]; + visibleColOrder[colIndex] = cid; + tHead.enableColumn(cid, colIndex); + colIndex++; + } + + tHead.setVisible(showColHeaders); + + } + + /** + * @param uidl + * which contains row data + * @param firstRow + * first row in data set + * @param reqRows + * amount of rows in data set + */ + private void updateBody(UIDL uidl, int firstRow, int reqRows) { + if (uidl == null || reqRows < 1) { + // container is empty, remove possibly existing rows + if (firstRow < 0) { + while (tBody.getLastRendered() > tBody.firstRendered) { + tBody.unlinkRow(false); + } + tBody.unlinkRow(false); + } + return; + } + + tBody.renderRows(uidl, firstRow, reqRows); + + final int optimalFirstRow = (int) (firstRowInViewPort - pageLength + * CACHE_RATE); + boolean cont = true; + while (cont && tBody.getLastRendered() > optimalFirstRow + && tBody.getFirstRendered() < optimalFirstRow) { + // client.console.log("removing row from start"); + cont = tBody.unlinkRow(true); + } + final int optimalLastRow = (int) (firstRowInViewPort + pageLength + pageLength + * CACHE_RATE); + cont = true; + while (cont && tBody.getLastRendered() > optimalLastRow) { + // client.console.log("removing row from the end"); + cont = tBody.unlinkRow(false); + } + tBody.fixSpacers(); + + } + + /** + * Gives correct column index for given column key ("cid" in UIDL). + * + * @param colKey + * @return column index of visible columns, -1 if column not visible + */ + private int getColIndexByKey(String colKey) { + // return 0 if asked for rowHeaders + if ("0".equals(colKey)) { + return 0; + } + for (int i = 0; i < visibleColOrder.length; i++) { + if (visibleColOrder[i].equals(colKey)) { + return i; + } + } + return -1; + } + + private boolean isCollapsedColumn(String colKey) { + if (collapsedColumns == null) { + return false; + } + if (collapsedColumns.contains(colKey)) { + return true; + } + return false; + } + + private String getColKeyByIndex(int index) { + return tHead.getHeaderCell(index).getColKey(); + } + + private void setColWidth(int colIndex, int w, boolean isDefinedWidth) { + final HeaderCell cell = tHead.getHeaderCell(colIndex); + cell.setWidth(w, isDefinedWidth); + tBody.setColWidth(colIndex, w); + } + + private int getColWidth(String colKey) { + return tHead.getHeaderCell(colKey).getWidth(); + } + + private VScrollTableRow getRenderedRowByKey(String key) { + final Iterator it = tBody.iterator(); + VScrollTableRow r = null; + while (it.hasNext()) { + r = (VScrollTableRow) it.next(); + if (r.getKey().equals(key)) { + return r; + } + } + return null; + } + + private void reOrderColumn(String columnKey, int newIndex) { + + final int oldIndex = getColIndexByKey(columnKey); + + // Change header order + tHead.moveCell(oldIndex, newIndex); + + // Change body order + tBody.moveCol(oldIndex, newIndex); + + /* + * Build new columnOrder and update it to server Note that columnOrder + * also contains collapsed columns so we cannot directly build it from + * cells vector Loop the old columnOrder and append in order to new + * array unless on moved columnKey. On new index also put the moved key + * i == index on columnOrder, j == index on newOrder + */ + final String oldKeyOnNewIndex = visibleColOrder[newIndex]; + if (showRowHeaders) { + newIndex--; // columnOrder don't have rowHeader + } + // add back hidden rows, + for (int i = 0; i < columnOrder.length; i++) { + if (columnOrder[i].equals(oldKeyOnNewIndex)) { + break; // break loop at target + } + if (isCollapsedColumn(columnOrder[i])) { + newIndex++; + } + } + // finally we can build the new columnOrder for server + final String[] newOrder = new String[columnOrder.length]; + for (int i = 0, j = 0; j < newOrder.length; i++) { + if (j == newIndex) { + newOrder[j] = columnKey; + j++; + } + if (i == columnOrder.length) { + break; + } + if (columnOrder[i].equals(columnKey)) { + continue; + } + newOrder[j] = columnOrder[i]; + j++; + } + columnOrder = newOrder; + // also update visibleColumnOrder + int i = showRowHeaders ? 1 : 0; + for (int j = 0; j < newOrder.length; j++) { + final String cid = newOrder[j]; + if (!isCollapsedColumn(cid)) { + visibleColOrder[i++] = cid; + } + } + client.updateVariable(paintableId, "columnorder", columnOrder, false); + } + + @Override + protected void onAttach() { + super.onAttach(); + if (initialContentReceived) { + sizeInit(); + } + } + + @Override + protected void onDetach() { + rowRequestHandler.cancel(); + super.onDetach(); + // ensure that scrollPosElement will be detached + if (scrollPositionElement != null) { + final Element parent = DOM.getParent(scrollPositionElement); + if (parent != null) { + DOM.removeChild(parent, scrollPositionElement); + } + } + } + + /** + * Run only once when component is attached and received its initial + * content. This function : * Syncs headers and bodys "natural widths and + * saves the values. * Sets proper width and height * Makes deferred request + * to get some cache rows + */ + private void sizeInit() { + /* + * We will use browsers table rendering algorithm to find proper column + * widths. If content and header take less space than available, we will + * divide extra space relatively to each column which has not width set. + * + * Overflow pixels are added to last column. + */ + + Iterator headCells = tHead.iterator(); + int i = 0; + int totalExplicitColumnsWidths = 0; + int total = 0; + float expandRatioDivider = 0; + + final int[] widths = new int[tHead.visibleCells.size()]; + + tHead.enableBrowserIntelligence(); + // first loop: collect natural widths + while (headCells.hasNext()) { + final HeaderCell hCell = (HeaderCell) headCells.next(); + int w = hCell.getWidth(); + if (hCell.isDefinedWidth()) { + // server has defined column width explicitly + totalExplicitColumnsWidths += w; + } else { + if (hCell.getExpandRatio() > 0) { + expandRatioDivider += hCell.getExpandRatio(); + w = 0; + } else { + // get and store greater of header width and column width, + // and + // store it as a minimumn natural col width + w = hCell.getNaturalColumnWidth(i); + } + hCell.setNaturalMinimumColumnWidth(w); + } + widths[i] = w; + total += w; + i++; + } + + tHead.disableBrowserIntelligence(); + + boolean willHaveScrollbarz = willHaveScrollbars(); + + // fix "natural" width if width not set + if (width == null || "".equals(width)) { + int w = total; + w += tBody.getCellExtraWidth() * visibleColOrder.length; + if (willHaveScrollbarz) { + w += Util.getNativeScrollbarSize(); + } + setContentWidth(w); + } + + int availW = tBody.getAvailableWidth(); + if (BrowserInfo.get().isIE()) { + // Hey IE, are you really sure about this? + availW = tBody.getAvailableWidth(); + } + availW -= tBody.getCellExtraWidth() * visibleColOrder.length; + + if (willHaveScrollbarz) { + availW -= Util.getNativeScrollbarSize(); + } + + boolean needsReLayout = false; + + if (availW > total) { + // natural size is smaller than available space + final int extraSpace = availW - total; + final int totalWidthR = total - totalExplicitColumnsWidths; + if (totalWidthR > 0) { + needsReLayout = true; + + if (expandRatioDivider > 0) { + // visible columns have some active expand ratios, excess + // space is divided according to them + headCells = tHead.iterator(); + i = 0; + while (headCells.hasNext()) { + HeaderCell hCell = (HeaderCell) headCells.next(); + if (hCell.getExpandRatio() > 0) { + int w = widths[i]; + final int newSpace = (int) (extraSpace * (hCell + .getExpandRatio() / expandRatioDivider)); + w += newSpace; + widths[i] = w; + } + i++; + } + } else { + // now we will share this sum relatively to those without + // explicit width + headCells = tHead.iterator(); + i = 0; + while (headCells.hasNext()) { + HeaderCell hCell = (HeaderCell) headCells.next(); + if (!hCell.isDefinedWidth()) { + int w = widths[i]; + final int newSpace = extraSpace * w / totalWidthR; + w += newSpace; + widths[i] = w; + } + i++; + } + } + } + + } else { + // bodys size will be more than available and scrollbar will appear + } + + // last loop: set possibly modified values or reset if new tBody + i = 0; + headCells = tHead.iterator(); + while (headCells.hasNext()) { + final HeaderCell hCell = (HeaderCell) headCells.next(); + if (isNewBody || hCell.getWidth() == -1) { + final int w = widths[i]; + setColWidth(i, w, false); + } + i++; + } + if (needsReLayout) { + tBody.reLayoutComponents(); + } + + /* + * Fix "natural" height if height is not set. This must be after width + * fixing so the components' widths have been adjusted. + */ + if (height == null || "".equals(height)) { + /* + * We must force an update of the row height as this point as it + * might have been (incorrectly) calculated earlier + */ + if (pageLength == totalRows) { + /* + * A hack to support variable height rows when paging is off. + * Generally this is not supported by scrolltable. We want to + * show all rows so the bodyHeight should be equal to the table + * height. + */ + int bodyHeight = tBody.getOffsetHeight(); + bodyContainer.setHeight(bodyHeight + "px"); + Util.runWebkitOverflowAutoFix(bodyContainer.getElement()); + } else { + int bodyHeight = (tBody.getRowHeight(true) * pageLength); + bodyContainer.setHeight(bodyHeight + "px"); + } + } + + isNewBody = false; + + if (firstvisible > 0) { + // Deferred due some Firefox oddities. IE & Safari could survive + // without + DeferredCommand.addCommand(new Command() { + public void execute() { + bodyContainer.setScrollPosition(firstvisible + * tBody.getRowHeight()); + firstRowInViewPort = firstvisible; + } + }); + } + + if (enabled) { + // Do we need cache rows + if (tBody.getLastRendered() + 1 < firstRowInViewPort + pageLength + + CACHE_REACT_RATE * pageLength) { + if (totalRows - 1 > tBody.getLastRendered()) { + // fetch cache rows + rowRequestHandler + .setReqFirstRow(tBody.getLastRendered() + 1); + rowRequestHandler + .setReqRows((int) (pageLength * CACHE_RATE)); + rowRequestHandler.deferRowFetch(1); + } + } + } + initializedAndAttached = true; + } + + private boolean willHaveScrollbars() { + if (!(height != null && !height.equals(""))) { + if (pageLength < totalRows) { + return true; + } + } else { + int fakeheight = tBody.getRowHeight() * totalRows; + int availableHeight = bodyContainer.getElement().getPropertyInt( + "clientHeight"); + if (fakeheight > availableHeight) { + return true; + } + } + return false; + } + + /** + * This method has logic which rows needs to be requested from server when + * user scrolls + */ + public void onScroll(Widget widget, int scrollLeft, int scrollTop) { + if (!initializedAndAttached) { + return; + } + if (!enabled) { + bodyContainer.setScrollPosition(firstRowInViewPort + * tBody.getRowHeight()); + return; + } + + rowRequestHandler.cancel(); + + // fix headers horizontal scrolling + tHead.setHorizontalScrollPosition(scrollLeft); + + firstRowInViewPort = (int) Math.ceil(scrollTop + / (double) tBody.getRowHeight()); + + int postLimit = (int) (firstRowInViewPort + pageLength + pageLength + * CACHE_REACT_RATE); + if (postLimit > totalRows - 1) { + postLimit = totalRows - 1; + } + int preLimit = (int) (firstRowInViewPort - pageLength + * CACHE_REACT_RATE); + if (preLimit < 0) { + preLimit = 0; + } + final int lastRendered = tBody.getLastRendered(); + final int firstRendered = tBody.getFirstRendered(); + + if (postLimit <= lastRendered && preLimit >= firstRendered) { + // remember which firstvisible we requested, in case the server has + // a differing opinion + lastRequestedFirstvisible = firstRowInViewPort; + client.updateVariable(paintableId, "firstvisible", + firstRowInViewPort, false); + return; // scrolled withing "non-react area" + } + + if (firstRowInViewPort - pageLength * CACHE_RATE > lastRendered + || firstRowInViewPort + pageLength + pageLength * CACHE_RATE < firstRendered) { + // need a totally new set + rowRequestHandler + .setReqFirstRow((int) (firstRowInViewPort - pageLength + * CACHE_RATE)); + int last = firstRowInViewPort + (int) CACHE_RATE * pageLength + + pageLength; + if (last > totalRows) { + last = totalRows - 1; + } + rowRequestHandler.setReqRows(last + - rowRequestHandler.getReqFirstRow() + 1); + rowRequestHandler.deferRowFetch(); + return; + } + if (preLimit < firstRendered) { + // need some rows to the beginning of the rendered area + rowRequestHandler + .setReqFirstRow((int) (firstRowInViewPort - pageLength + * CACHE_RATE)); + rowRequestHandler.setReqRows(firstRendered + - rowRequestHandler.getReqFirstRow()); + rowRequestHandler.deferRowFetch(); + + return; + } + if (postLimit > lastRendered) { + // need some rows to the end of the rendered area + rowRequestHandler.setReqFirstRow(lastRendered + 1); + rowRequestHandler.setReqRows((int) ((firstRowInViewPort + + pageLength + pageLength * CACHE_RATE) - lastRendered)); + rowRequestHandler.deferRowFetch(); + } + + } + + private void announceScrollPosition() { + if (scrollPositionElement == null) { + scrollPositionElement = DOM.createDiv(); + DOM.setElementProperty(scrollPositionElement, "className", + "i-table-scrollposition"); + DOM.appendChild(getElement(), scrollPositionElement); + } + + DOM.setStyleAttribute(scrollPositionElement, "position", "absolute"); + DOM.setStyleAttribute(scrollPositionElement, "marginLeft", (DOM + .getElementPropertyInt(getElement(), "offsetWidth") / 2 - 80) + + "px"); + DOM.setStyleAttribute(scrollPositionElement, "marginTop", -(DOM + .getElementPropertyInt(getElement(), "offsetHeight") - 2) + + "px"); + + // indexes go from 1-totalRows, as rowheaders in index-mode indicate + int last = (firstRowInViewPort + (bodyContainer.getOffsetHeight() / tBody + .getRowHeight())); + if (last > totalRows) { + last = totalRows; + } + DOM.setInnerHTML(scrollPositionElement, "" + + (firstRowInViewPort + 1) + " – " + last + "..." + + ""); + DOM.setStyleAttribute(scrollPositionElement, "display", "block"); + } + + private void hideScrollPositionAnnotation() { + if (scrollPositionElement != null) { + DOM.setStyleAttribute(scrollPositionElement, "display", "none"); + } + } + + private class RowRequestHandler extends Timer { + + private int reqFirstRow = 0; + private int reqRows = 0; + + public void deferRowFetch() { + deferRowFetch(250); + } + + public void deferRowFetch(int msec) { + if (reqRows > 0 && reqFirstRow < totalRows) { + schedule(msec); + + // tell scroll position to user if currently "visible" rows are + // not rendered + if ((firstRowInViewPort + pageLength > tBody.getLastRendered()) + || (firstRowInViewPort < tBody.getFirstRendered())) { + announceScrollPosition(); + } else { + hideScrollPositionAnnotation(); + } + } + } + + public void setReqFirstRow(int reqFirstRow) { + if (reqFirstRow < 0) { + reqFirstRow = 0; + } else if (reqFirstRow >= totalRows) { + reqFirstRow = totalRows - 1; + } + this.reqFirstRow = reqFirstRow; + } + + public void setReqRows(int reqRows) { + this.reqRows = reqRows; + } + + @Override + public void run() { + if (client.hasActiveRequest()) { + // if client connection is busy, don't bother loading it more + schedule(250); + + } else { + + int firstToBeRendered = tBody.firstRendered; + if (reqFirstRow < firstToBeRendered) { + firstToBeRendered = reqFirstRow; + } else if (firstRowInViewPort - (int) (CACHE_RATE * pageLength) > firstToBeRendered) { + firstToBeRendered = firstRowInViewPort + - (int) (CACHE_RATE * pageLength); + if (firstToBeRendered < 0) { + firstToBeRendered = 0; + } + } + + int lastToBeRendered = tBody.lastRendered; + + if (reqFirstRow + reqRows - 1 > lastToBeRendered) { + lastToBeRendered = reqFirstRow + reqRows - 1; + } else if (firstRowInViewPort + pageLength + pageLength + * CACHE_RATE < lastToBeRendered) { + lastToBeRendered = (firstRowInViewPort + pageLength + (int) (pageLength * CACHE_RATE)); + if (lastToBeRendered >= totalRows) { + lastToBeRendered = totalRows - 1; + } + // due Safari 3.1 bug (see #2607), verify reqrows, original + // problem unknown, but this should catch the issue + if (reqFirstRow + reqRows - 1 > lastToBeRendered) { + reqRows = lastToBeRendered - reqFirstRow; + } + } + + client.updateVariable(paintableId, "firstToBeRendered", + firstToBeRendered, false); + + client.updateVariable(paintableId, "lastToBeRendered", + lastToBeRendered, false); + // remember which firstvisible we requested, in case the server + // has + // a differing opinion + lastRequestedFirstvisible = firstRowInViewPort; + client.updateVariable(paintableId, "firstvisible", + firstRowInViewPort, false); + client.updateVariable(paintableId, "reqfirstrow", reqFirstRow, + false); + client.updateVariable(paintableId, "reqrows", reqRows, true); + + } + } + + public int getReqFirstRow() { + return reqFirstRow; + } + + public int getReqRows() { + return reqRows; + } + + /** + * Sends request to refresh content at this position. + */ + public void refreshContent() { + int first = (int) (firstRowInViewPort - pageLength * CACHE_RATE); + int reqRows = (int) (2 * pageLength * CACHE_RATE + pageLength); + if (first < 0) { + reqRows = reqRows + first; + first = 0; + } + setReqFirstRow(first); + setReqRows(reqRows); + run(); + } + } + + public class HeaderCell extends Widget { + + Element td = DOM.createTD(); + + Element captionContainer = DOM.createDiv(); + + Element colResizeWidget = DOM.createDiv(); + + Element floatingCopyOfHeaderCell; + + private boolean sortable = false; + private final String cid; + private boolean dragging; + + private int dragStartX; + private int colIndex; + private int originalWidth; + + private boolean isResizing; + + private int headerX; + + private boolean moved; + + private int closestSlot; + + private int width = -1; + + private int naturalWidth = -1; + + private char align = ALIGN_LEFT; + + boolean definedWidth = false; + + private float expandRatio = 0; + + public void setSortable(boolean b) { + sortable = b; + } + + public void setNaturalMinimumColumnWidth(int w) { + naturalWidth = w; + } + + public HeaderCell(String colId, String headerText) { + cid = colId; + + DOM.setElementProperty(colResizeWidget, "className", CLASSNAME + + "-resizer"); + DOM.sinkEvents(colResizeWidget, Event.MOUSEEVENTS); + + setText(headerText); + + DOM.appendChild(td, colResizeWidget); + + DOM.setElementProperty(captionContainer, "className", CLASSNAME + + "-caption-container"); + + // ensure no clipping initially (problem on column additions) + DOM.setStyleAttribute(captionContainer, "overflow", "visible"); + + DOM.sinkEvents(captionContainer, Event.MOUSEEVENTS); + + DOM.appendChild(td, captionContainer); + + DOM.sinkEvents(td, Event.MOUSEEVENTS); + + setElement(td); + } + + public void setWidth(int w, boolean ensureDefinedWidth) { + if (ensureDefinedWidth) { + definedWidth = true; + // on column resize expand ratio becomes zero + expandRatio = 0; + } + if (width == w) { + return; + } + if (width == -1) { + // go to default mode, clip content if necessary + DOM.setStyleAttribute(captionContainer, "overflow", ""); + } + width = w; + if (w == -1) { + DOM.setStyleAttribute(captionContainer, "width", ""); + setWidth(""); + } else { + + captionContainer.getStyle().setPropertyPx("width", w); + + /* + * if we already have tBody, set the header width properly, if + * not defer it. IE will fail with complex float in table header + * unless TD width is not explicitly set. + */ + if (tBody != null) { + int tdWidth = width + tBody.getCellExtraWidth(); + setWidth(tdWidth + "px"); + } else { + DeferredCommand.addCommand(new Command() { + public void execute() { + int tdWidth = width + tBody.getCellExtraWidth(); + setWidth(tdWidth + "px"); + } + }); + } + } + } + + public void setUndefinedWidth() { + definedWidth = false; + setWidth(-1, false); + } + + /** + * Detects if width is fixed by developer on server side or resized to + * current width by user. + * + * @return true if defined, false if "natural" width + */ + public boolean isDefinedWidth() { + return definedWidth; + } + + public int getWidth() { + return width; + } + + public void setText(String headerText) { + DOM.setInnerHTML(captionContainer, headerText); + } + + public String getColKey() { + return cid; + } + + private void setSorted(boolean sorted) { + if (sorted) { + if (sortAscending) { + this.setStyleName(CLASSNAME + "-header-cell-asc"); + } else { + this.setStyleName(CLASSNAME + "-header-cell-desc"); + } + } else { + this.setStyleName(CLASSNAME + "-header-cell"); + } + } + + /** + * Handle column reordering. + */ + @Override + public void onBrowserEvent(Event event) { + if (enabled && event != null) { + if (isResizing || event.getTarget() == colResizeWidget) { + onResizeEvent(event); + } else { + handleCaptionEvent(event); + } + } + } + + private void createFloatingCopy() { + floatingCopyOfHeaderCell = DOM.createDiv(); + DOM.setInnerHTML(floatingCopyOfHeaderCell, DOM.getInnerHTML(td)); + floatingCopyOfHeaderCell = DOM + .getChild(floatingCopyOfHeaderCell, 1); + DOM.setElementProperty(floatingCopyOfHeaderCell, "className", + CLASSNAME + "-header-drag"); + updateFloatingCopysPosition(DOM.getAbsoluteLeft(td), DOM + .getAbsoluteTop(td)); + DOM.appendChild(RootPanel.get().getElement(), + floatingCopyOfHeaderCell); + } + + private void updateFloatingCopysPosition(int x, int y) { + x -= DOM.getElementPropertyInt(floatingCopyOfHeaderCell, + "offsetWidth") / 2; + DOM.setStyleAttribute(floatingCopyOfHeaderCell, "left", x + "px"); + if (y > 0) { + DOM.setStyleAttribute(floatingCopyOfHeaderCell, "top", (y + 7) + + "px"); + } + } + + private void hideFloatingCopy() { + DOM.removeChild(RootPanel.get().getElement(), + floatingCopyOfHeaderCell); + floatingCopyOfHeaderCell = null; + } + + protected void handleCaptionEvent(Event event) { + switch (DOM.eventGetType(event)) { + case Event.ONMOUSEDOWN: + if (columnReordering) { + dragging = true; + moved = false; + colIndex = getColIndexByKey(cid); + DOM.setCapture(getElement()); + headerX = tHead.getAbsoluteLeft(); + DOM.eventPreventDefault(event); // prevent selecting text + } + break; + case Event.ONMOUSEUP: + if (columnReordering) { + dragging = false; + DOM.releaseCapture(getElement()); + if (moved) { + hideFloatingCopy(); + tHead.removeSlotFocus(); + if (closestSlot != colIndex + && closestSlot != (colIndex + 1)) { + if (closestSlot > colIndex) { + reOrderColumn(cid, closestSlot - 1); + } else { + reOrderColumn(cid, closestSlot); + } + } + } + } + + if (!moved) { + // mouse event was a click to header -> sort column + if (sortable) { + if (sortColumn.equals(cid)) { + // just toggle order + client.updateVariable(paintableId, "sortascending", + !sortAscending, false); + } else { + // set table scrolled by this column + client.updateVariable(paintableId, "sortcolumn", + cid, false); + } + // get also cache columns at the same request + bodyContainer.setScrollPosition(0); + firstvisible = 0; + rowRequestHandler.setReqFirstRow(0); + rowRequestHandler.setReqRows((int) (2 * pageLength + * CACHE_RATE + pageLength)); + rowRequestHandler.deferRowFetch(); + } + break; + } + break; + case Event.ONMOUSEMOVE: + if (dragging) { + if (!moved) { + createFloatingCopy(); + moved = true; + } + final int x = DOM.eventGetClientX(event) + + DOM.getElementPropertyInt(tHead.hTableWrapper, + "scrollLeft"); + int slotX = headerX; + closestSlot = colIndex; + int closestDistance = -1; + int start = 0; + if (showRowHeaders) { + start++; + } + final int visibleCellCount = tHead.getVisibleCellCount(); + for (int i = start; i <= visibleCellCount; i++) { + if (i > 0) { + final String colKey = getColKeyByIndex(i - 1); + slotX += getColWidth(colKey); + } + final int dist = Math.abs(x - slotX); + if (closestDistance == -1 || dist < closestDistance) { + closestDistance = dist; + closestSlot = i; + } + } + tHead.focusSlot(closestSlot); + + updateFloatingCopysPosition(DOM.eventGetClientX(event), -1); + } + break; + default: + break; + } + } + + private void onResizeEvent(Event event) { + switch (DOM.eventGetType(event)) { + case Event.ONMOUSEDOWN: + isResizing = true; + DOM.setCapture(getElement()); + dragStartX = DOM.eventGetClientX(event); + colIndex = getColIndexByKey(cid); + originalWidth = getWidth(); + DOM.eventPreventDefault(event); + break; + case Event.ONMOUSEUP: + isResizing = false; + DOM.releaseCapture(getElement()); + // readjust undefined width columns + lazyAdjustColumnWidths.cancel(); + lazyAdjustColumnWidths.schedule(1); + break; + case Event.ONMOUSEMOVE: + if (isResizing) { + final int deltaX = DOM.eventGetClientX(event) - dragStartX; + if (deltaX == 0) { + return; + } + + int newWidth = originalWidth + deltaX; + if (newWidth < tBody.getCellExtraWidth()) { + newWidth = tBody.getCellExtraWidth(); + } + setColWidth(colIndex, newWidth, true); + } + break; + default: + break; + } + } + + public String getCaption() { + return DOM.getInnerText(captionContainer); + } + + public boolean isEnabled() { + return getParent() != null; + } + + public void setAlign(char c) { + if (align != c) { + switch (c) { + case ALIGN_CENTER: + DOM.setStyleAttribute(captionContainer, "textAlign", + "center"); + break; + case ALIGN_RIGHT: + DOM.setStyleAttribute(captionContainer, "textAlign", + "right"); + break; + default: + DOM.setStyleAttribute(captionContainer, "textAlign", ""); + break; + } + } + align = c; + } + + public char getAlign() { + return align; + } + + /** + * Detects the natural minimum width for the column of this header cell. + * If column is resized by user or the width is defined by server the + * actual width is returned. Else the natural min width is returned. + * + * @param columnIndex + * column index hint, if -1 (unknown) it will be detected + * + * @return + */ + public int getNaturalColumnWidth(int columnIndex) { + if (isDefinedWidth()) { + return width; + } else { + if (naturalWidth < 0) { + // This is recently revealed column. Try to detect a proper + // value (greater of header and data + // cols) + + final int hw = ((Element) getElement().getLastChild()) + .getOffsetWidth() + + tBody.getCellExtraWidth(); + if (columnIndex < 0) { + columnIndex = 0; + for (Iterator it = tHead.iterator(); it + .hasNext(); columnIndex++) { + if (it.next() == this) { + break; + } + } + } + final int cw = tBody.getColWidth(columnIndex); + naturalWidth = (hw > cw ? hw : cw); + } + return naturalWidth; + } + } + + public void setExpandRatio(float floatAttribute) { + expandRatio = floatAttribute; + } + + public float getExpandRatio() { + return expandRatio; + } + + } + + /** + * HeaderCell that is header cell for row headers. + * + * Reordering disabled and clicking on it resets sorting. + */ + public class RowHeadersHeaderCell extends HeaderCell { + + RowHeadersHeaderCell() { + super("0", ""); + } + + @Override + protected void handleCaptionEvent(Event event) { + // NOP: RowHeaders cannot be reordered + // TODO It'd be nice to reset sorting here + } + } + + public class TableHead extends Panel implements ActionOwner { + + private static final int WRAPPER_WIDTH = 9000; + + Vector visibleCells = new Vector(); + + HashMap availableCells = new HashMap(); + + Element div = DOM.createDiv(); + Element hTableWrapper = DOM.createDiv(); + Element hTableContainer = DOM.createDiv(); + Element table = DOM.createTable(); + Element headerTableBody = DOM.createTBody(); + Element tr = DOM.createTR(); + + private final Element columnSelector = DOM.createDiv(); + + private int focusedSlot = -1; + + public TableHead() { + if (BrowserInfo.get().isIE()) { + table.setPropertyInt("cellSpacing", 0); + } + + DOM.setStyleAttribute(hTableWrapper, "overflow", "hidden"); + DOM.setElementProperty(hTableWrapper, "className", CLASSNAME + + "-header"); + + // TODO move styles to CSS + DOM.setElementProperty(columnSelector, "className", CLASSNAME + + "-column-selector"); + DOM.setStyleAttribute(columnSelector, "display", "none"); + + DOM.appendChild(table, headerTableBody); + DOM.appendChild(headerTableBody, tr); + DOM.appendChild(hTableContainer, table); + DOM.appendChild(hTableWrapper, hTableContainer); + DOM.appendChild(div, hTableWrapper); + DOM.appendChild(div, columnSelector); + setElement(div); + + setStyleName(CLASSNAME + "-header-wrap"); + + DOM.sinkEvents(columnSelector, Event.ONCLICK); + + availableCells.put("0", new RowHeadersHeaderCell()); + } + + @Override + public void clear() { + for (String cid : availableCells.keySet()) { + removeCell(cid); + } + availableCells.clear(); + availableCells.put("0", new RowHeadersHeaderCell()); + } + + public void updateCellsFromUIDL(UIDL uidl) { + Iterator it = uidl.getChildIterator(); + HashSet updated = new HashSet(); + updated.add("0"); + while (it.hasNext()) { + final UIDL col = (UIDL) it.next(); + final String cid = col.getStringAttribute("cid"); + updated.add(cid); + + String caption = buildCaptionHtmlSnippet(col); + HeaderCell c = getHeaderCell(cid); + if (c == null) { + c = new HeaderCell(cid, caption); + availableCells.put(cid, c); + if (initializedAndAttached) { + // we will need a column width recalculation + initializedAndAttached = false; + initialContentReceived = false; + isNewBody = true; + } + } else { + c.setText(caption); + } + + if (col.hasAttribute("sortable")) { + c.setSortable(true); + if (cid.equals(sortColumn)) { + c.setSorted(true); + } else { + c.setSorted(false); + } + } else { + c.setSortable(false); + } + + if (col.hasAttribute("align")) { + c.setAlign(col.getStringAttribute("align").charAt(0)); + } + if (col.hasAttribute("width")) { + final String width = col.getStringAttribute("width"); + c.setWidth(Integer.parseInt(width), true); + } else if (recalcWidths) { + c.setUndefinedWidth(); + } + if (col.hasAttribute("er")) { + c.setExpandRatio(col.getFloatAttribute("er")); + } + } + // check for orphaned header cells + for (String cid : availableCells.keySet()) { + if (!updated.contains(cid)) { + removeCell(cid); + it.remove(); + } + } + + } + + public void enableColumn(String cid, int index) { + final HeaderCell c = getHeaderCell(cid); + if (!c.isEnabled() || getHeaderCell(index) != c) { + setHeaderCell(index, c); + if (initializedAndAttached) { + headerChangedDuringUpdate = true; + } + } + } + + public int getVisibleCellCount() { + return visibleCells.size(); + } + + public void setHorizontalScrollPosition(int scrollLeft) { + DOM.setElementPropertyInt(hTableWrapper, "scrollLeft", scrollLeft); + } + + public void setColumnCollapsingAllowed(boolean cc) { + if (cc) { + DOM.setStyleAttribute(columnSelector, "display", "block"); + } else { + DOM.setStyleAttribute(columnSelector, "display", "none"); + } + } + + public void disableBrowserIntelligence() { + DOM.setStyleAttribute(hTableContainer, "width", WRAPPER_WIDTH + + "px"); + } + + public void enableBrowserIntelligence() { + DOM.setStyleAttribute(hTableContainer, "width", ""); + } + + public void setHeaderCell(int index, HeaderCell cell) { + if (cell.isEnabled()) { + // we're moving the cell + DOM.removeChild(tr, cell.getElement()); + orphan(cell); + } + if (index < visibleCells.size()) { + // insert to right slot + DOM.insertChild(tr, cell.getElement(), index); + adopt(cell); + visibleCells.insertElementAt(cell, index); + + } else if (index == visibleCells.size()) { + // simply append + DOM.appendChild(tr, cell.getElement()); + adopt(cell); + visibleCells.add(cell); + } else { + throw new RuntimeException( + "Header cells must be appended in order"); + } + } + + public HeaderCell getHeaderCell(int index) { + if (index < visibleCells.size()) { + return (HeaderCell) visibleCells.get(index); + } else { + return null; + } + } + + /** + * Get's HeaderCell by it's column Key. + * + * Note that this returns HeaderCell even if it is currently collapsed. + * + * @param cid + * Column key of accessed HeaderCell + * @return HeaderCell + */ + public HeaderCell getHeaderCell(String cid) { + return availableCells.get(cid); + } + + public void moveCell(int oldIndex, int newIndex) { + final HeaderCell hCell = getHeaderCell(oldIndex); + final Element cell = hCell.getElement(); + + visibleCells.remove(oldIndex); + DOM.removeChild(tr, cell); + + DOM.insertChild(tr, cell, newIndex); + visibleCells.insertElementAt(hCell, newIndex); + } + + public Iterator iterator() { + return visibleCells.iterator(); + } + + @Override + public boolean remove(Widget w) { + if (visibleCells.contains(w)) { + visibleCells.remove(w); + orphan(w); + DOM.removeChild(DOM.getParent(w.getElement()), w.getElement()); + return true; + } + return false; + } + + public void removeCell(String colKey) { + final HeaderCell c = getHeaderCell(colKey); + remove(c); + } + + private void focusSlot(int index) { + removeSlotFocus(); + if (index > 0) { + DOM.setElementProperty(DOM.getFirstChild(DOM.getChild(tr, + index - 1)), "className", CLASSNAME + "-resizer " + + CLASSNAME + "-focus-slot-right"); + } else { + DOM.setElementProperty(DOM.getFirstChild(DOM + .getChild(tr, index)), "className", CLASSNAME + + "-resizer " + CLASSNAME + "-focus-slot-left"); + } + focusedSlot = index; + } + + private void removeSlotFocus() { + if (focusedSlot < 0) { + return; + } + if (focusedSlot == 0) { + DOM.setElementProperty(DOM.getFirstChild(DOM.getChild(tr, + focusedSlot)), "className", CLASSNAME + "-resizer"); + } else if (focusedSlot > 0) { + DOM.setElementProperty(DOM.getFirstChild(DOM.getChild(tr, + focusedSlot - 1)), "className", CLASSNAME + "-resizer"); + } + focusedSlot = -1; + } + + @Override + public void onBrowserEvent(Event event) { + if (enabled) { + if (event.getTarget() == columnSelector) { + final int left = DOM.getAbsoluteLeft(columnSelector); + final int top = DOM.getAbsoluteTop(columnSelector) + + DOM.getElementPropertyInt(columnSelector, + "offsetHeight"); + client.getContextMenu().showAt(this, left, top); + } + } + } + + class VisibleColumnAction extends Action { + + String colKey; + private boolean collapsed; + + public VisibleColumnAction(String colKey) { + super(VScrollTable.TableHead.this); + this.colKey = colKey; + caption = tHead.getHeaderCell(colKey).getCaption(); + } + + @Override + public void execute() { + client.getContextMenu().hide(); + // toggle selected column + if (collapsedColumns.contains(colKey)) { + collapsedColumns.remove(colKey); + } else { + tHead.removeCell(colKey); + collapsedColumns.add(colKey); + lazyAdjustColumnWidths.schedule(1); + } + + // update variable to server + client.updateVariable(paintableId, "collapsedcolumns", + collapsedColumns.toArray(), false); + // let rowRequestHandler determine proper rows + rowRequestHandler.refreshContent(); + } + + public void setCollapsed(boolean b) { + collapsed = b; + } + + /** + * Override default method to distinguish on/off columns + */ + @Override + public String getHTML() { + final StringBuffer buf = new StringBuffer(); + if (collapsed) { + buf.append(""); + } else { + buf.append(""); + } + buf.append(super.getHTML()); + buf.append(""); + + return buf.toString(); + } + + } + + /* + * Returns columns as Action array for column select popup + */ + public Action[] getActions() { + Object[] cols; + if (columnReordering) { + cols = columnOrder; + } else { + // if columnReordering is disabled, we need different way to get + // all available columns + cols = visibleColOrder; + cols = new Object[visibleColOrder.length + + collapsedColumns.size()]; + int i; + for (i = 0; i < visibleColOrder.length; i++) { + cols[i] = visibleColOrder[i]; + } + for (final Iterator it = collapsedColumns.iterator(); it + .hasNext();) { + cols[i++] = it.next(); + } + } + final Action[] actions = new Action[cols.length]; + + for (int i = 0; i < cols.length; i++) { + final String cid = (String) cols[i]; + final HeaderCell c = getHeaderCell(cid); + final VisibleColumnAction a = new VisibleColumnAction(c + .getColKey()); + a.setCaption(c.getCaption()); + if (!c.isEnabled()) { + a.setCollapsed(true); + } + actions[i] = a; + } + return actions; + } + + public ApplicationConnection getClient() { + return client; + } + + public String getPaintableId() { + return paintableId; + } + + /** + * Returns column alignments for visible columns + */ + public char[] getColumnAlignments() { + final Iterator it = visibleCells.iterator(); + final char[] aligns = new char[visibleCells.size()]; + int colIndex = 0; + while (it.hasNext()) { + aligns[colIndex++] = ((HeaderCell) it.next()).getAlign(); + } + return aligns; + } + + } + + /** + * This Panel can only contain VScrollTableRow type of widgets. This + * "simulates" very large table, keeping spacers which take room of + * unrendered rows. + * + */ + public class VScrollTableBody extends Panel { + + public static final int DEFAULT_ROW_HEIGHT = 24; + + private int rowHeight = -1; + + private final List renderedRows = new Vector(); + + /** + * Due some optimizations row height measuring is deferred and initial + * set of rows is rendered detached. Flag set on when table body has + * been attached in dom and rowheight has been measured. + */ + private boolean tBodyMeasurementsDone = false; + + Element preSpacer = DOM.createDiv(); + Element postSpacer = DOM.createDiv(); + + Element container = DOM.createDiv(); + + TableSectionElement tBodyElement = Document.get().createTBodyElement(); + Element table = DOM.createTable(); + + private int firstRendered; + + private int lastRendered; + + private char[] aligns; + + VScrollTableBody() { + constructDOM(); + + setElement(container); + } + + private void constructDOM() { + DOM.setElementProperty(table, "className", CLASSNAME + "-table"); + if (BrowserInfo.get().isIE()) { + table.setPropertyInt("cellSpacing", 0); + } + DOM.setElementProperty(preSpacer, "className", CLASSNAME + + "-row-spacer"); + DOM.setElementProperty(postSpacer, "className", CLASSNAME + + "-row-spacer"); + + table.appendChild(tBodyElement); + DOM.appendChild(container, preSpacer); + DOM.appendChild(container, table); + DOM.appendChild(container, postSpacer); + + } + + public int getAvailableWidth() { + int availW = bodyContainer.getOffsetWidth() - getBorderWidth(); + return availW; + } + + public void renderInitialRows(UIDL rowData, int firstIndex, int rows) { + firstRendered = firstIndex; + lastRendered = firstIndex + rows - 1; + final Iterator it = rowData.getChildIterator(); + aligns = tHead.getColumnAlignments(); + while (it.hasNext()) { + final VScrollTableRow row = new VScrollTableRow((UIDL) it + .next(), aligns); + addRow(row); + } + if (isAttached()) { + fixSpacers(); + } + } + + public void renderRows(UIDL rowData, int firstIndex, int rows) { + // FIXME REVIEW + aligns = tHead.getColumnAlignments(); + final Iterator it = rowData.getChildIterator(); + if (firstIndex == lastRendered + 1) { + while (it.hasNext()) { + final VScrollTableRow row = createRow((UIDL) it.next()); + addRow(row); + lastRendered++; + } + fixSpacers(); + } else if (firstIndex + rows == firstRendered) { + final VScrollTableRow[] rowArray = new VScrollTableRow[rows]; + int i = rows; + while (it.hasNext()) { + i--; + rowArray[i] = createRow((UIDL) it.next()); + } + for (i = 0; i < rows; i++) { + addRowBeforeFirstRendered(rowArray[i]); + firstRendered--; + } + } else { + // completely new set of rows + while (lastRendered + 1 > firstRendered) { + unlinkRow(false); + } + final VScrollTableRow row = createRow((UIDL) it.next()); + firstRendered = firstIndex; + lastRendered = firstIndex - 1; + addRow(row); + lastRendered++; + setContainerHeight(); + fixSpacers(); + while (it.hasNext()) { + addRow(createRow((UIDL) it.next())); + lastRendered++; + } + fixSpacers(); + } + // this may be a new set of rows due content change, + // ensure we have proper cache rows + int reactFirstRow = (int) (firstRowInViewPort - pageLength + * CACHE_REACT_RATE); + int reactLastRow = (int) (firstRowInViewPort + pageLength + pageLength + * CACHE_REACT_RATE); + if (reactFirstRow < 0) { + reactFirstRow = 0; + } + if (reactLastRow > totalRows) { + reactLastRow = totalRows - 1; + } + if (lastRendered < reactLastRow) { + // get some cache rows below visible area + rowRequestHandler.setReqFirstRow(lastRendered + 1); + rowRequestHandler.setReqRows(reactLastRow - lastRendered - 1); + rowRequestHandler.deferRowFetch(1); + } else if (tBody.getFirstRendered() > reactFirstRow) { + /* + * Branch for fetching cache above visible area. + * + * If cache needed for both before and after visible area, this + * will be rendered after-cache is reveived and rendered. So in + * some rare situations table may take two cache visits to + * server. + */ + rowRequestHandler.setReqFirstRow(reactFirstRow); + rowRequestHandler.setReqRows(firstRendered - reactFirstRow); + rowRequestHandler.deferRowFetch(1); + } + } + + /** + * This method is used to instantiate new rows for this table. It + * automatically sets correct widths to rows cells and assigns correct + * client reference for child widgets. + * + * This method can be called only after table has been initialized + * + * @param uidl + */ + private VScrollTableRow createRow(UIDL uidl) { + final VScrollTableRow row = new VScrollTableRow(uidl, aligns); + final int cells = DOM.getChildCount(row.getElement()); + for (int i = 0; i < cells; i++) { + final Element cell = DOM.getChild(row.getElement(), i); + int w = VScrollTable.this.getColWidth(getColKeyByIndex(i)); + if (w < 0) { + w = 0; + } + cell.getFirstChildElement().getStyle() + .setPropertyPx("width", w); + cell.getStyle().setPropertyPx("width", w); + } + return row; + } + + private void addRowBeforeFirstRendered(VScrollTableRow row) { + VScrollTableRow first = null; + if (renderedRows.size() > 0) { + first = (VScrollTableRow) renderedRows.get(0); + } + if (first != null && first.getStyleName().indexOf("-odd") == -1) { + row.addStyleName(CLASSNAME + "-row-odd"); + } else { + row.addStyleName(CLASSNAME + "-row"); + } + if (row.isSelected()) { + row.addStyleName("i-selected"); + } + tBodyElement.insertBefore(row.getElement(), tBodyElement + .getFirstChild()); + adopt(row); + renderedRows.add(0, row); + } + + private void addRow(VScrollTableRow row) { + VScrollTableRow last = null; + if (renderedRows.size() > 0) { + last = (VScrollTableRow) renderedRows + .get(renderedRows.size() - 1); + } + if (last != null && last.getStyleName().indexOf("-odd") == -1) { + row.addStyleName(CLASSNAME + "-row-odd"); + } else { + row.addStyleName(CLASSNAME + "-row"); + } + if (row.isSelected()) { + row.addStyleName("i-selected"); + } + tBodyElement.appendChild(row.getElement()); + adopt(row); + renderedRows.add(row); + } + + public Iterator iterator() { + return renderedRows.iterator(); + } + + /** + * @return false if couldn't remove row + */ + public boolean unlinkRow(boolean fromBeginning) { + if (lastRendered - firstRendered < 0) { + return false; + } + int index; + if (fromBeginning) { + index = 0; + firstRendered++; + } else { + index = renderedRows.size() - 1; + lastRendered--; + } + if (index >= 0) { + final VScrollTableRow toBeRemoved = (VScrollTableRow) renderedRows + .get(index); + lazyUnregistryBag.add(toBeRemoved); + tBodyElement.removeChild(toBeRemoved.getElement()); + orphan(toBeRemoved); + renderedRows.remove(index); + fixSpacers(); + return true; + } else { + return false; + } + } + + @Override + public boolean remove(Widget w) { + throw new UnsupportedOperationException(); + } + + @Override + protected void onAttach() { + super.onAttach(); + setContainerHeight(); + } + + /** + * Fix container blocks height according to totalRows to avoid + * "bouncing" when scrolling + */ + private void setContainerHeight() { + fixSpacers(); + DOM.setStyleAttribute(container, "height", totalRows + * getRowHeight() + "px"); + } + + private void fixSpacers() { + int prepx = getRowHeight() * firstRendered; + if (prepx < 0) { + prepx = 0; + } + DOM.setStyleAttribute(preSpacer, "height", prepx + "px"); + int postpx = getRowHeight() * (totalRows - 1 - lastRendered); + if (postpx < 0) { + postpx = 0; + } + DOM.setStyleAttribute(postSpacer, "height", postpx + "px"); + } + + public int getRowHeight() { + return getRowHeight(false); + } + + public int getRowHeight(boolean forceUpdate) { + if (tBodyMeasurementsDone && !forceUpdate) { + return rowHeight; + } else { + + if (tBodyElement.getRows().getLength() > 0) { + rowHeight = getTableHeight() + / tBodyElement.getRows().getLength(); + } else { + if (isAttached()) { + // measure row height by adding a dummy row + VScrollTableRow scrollTableRow = new VScrollTableRow(); + tBodyElement.appendChild(scrollTableRow.getElement()); + getRowHeight(forceUpdate); + tBodyElement.removeChild(scrollTableRow.getElement()); + } else { + // TODO investigate if this can never happen anymore + return DEFAULT_ROW_HEIGHT; + } + } + tBodyMeasurementsDone = true; + return rowHeight; + } + } + + public int getTableHeight() { + return table.getOffsetHeight(); + } + + /** + * Returns the width available for column content. + * + * @param columnIndex + * @return + */ + public int getColWidth(int columnIndex) { + if (tBodyMeasurementsDone) { + NodeList rows = tBodyElement.getRows(); + if (rows.getLength() == 0) { + // no rows yet rendered + return 0; + } else { + com.google.gwt.dom.client.Element wrapperdiv = rows + .getItem(0).getCells().getItem(columnIndex) + .getFirstChildElement(); + return wrapperdiv.getOffsetWidth(); + } + } else { + return 0; + } + } + + /** + * Sets the content width of a column. + * + * Due IE limitation, we must set the width to a wrapper elements inside + * table cells (with overflow hidden, which does not work on td + * elements). + * + * To get this work properly crossplatform, we will also set the width + * of td. + * + * @param colIndex + * @param w + */ + public void setColWidth(int colIndex, int w) { + NodeList rows2 = tBodyElement.getRows(); + final int rows = rows2.getLength(); + for (int i = 0; i < rows; i++) { + TableRowElement row = rows2.getItem(i); + TableCellElement cell = row.getCells().getItem(colIndex); + cell.getFirstChildElement().getStyle() + .setPropertyPx("width", w); + cell.getStyle().setPropertyPx("width", w); + } + } + + private int cellExtraWidth = -1; + + /** + * Method to return the space used for cell paddings + border. + */ + private int getCellExtraWidth() { + if (cellExtraWidth < 0) { + detectExtrawidth(); + } + return cellExtraWidth; + } + + private void detectExtrawidth() { + NodeList rows = tBodyElement.getRows(); + if (rows.getLength() == 0) { + /* need to temporary add empty row and detect */ + VScrollTableRow scrollTableRow = new VScrollTableRow(); + tBodyElement.appendChild(scrollTableRow.getElement()); + detectExtrawidth(); + tBodyElement.removeChild(scrollTableRow.getElement()); + } else { + TableRowElement item = rows.getItem(0); + TableCellElement firstTD = item.getCells().getItem(0); + com.google.gwt.dom.client.Element wrapper = firstTD + .getFirstChildElement(); + cellExtraWidth = firstTD.getOffsetWidth() + - wrapper.getOffsetWidth(); + } + } + + private void reLayoutComponents() { + for (Widget w : this) { + VScrollTableRow r = (VScrollTableRow) w; + for (Widget widget : r) { + client.handleComponentRelativeSize(widget); + } + } + } + + public int getLastRendered() { + return lastRendered; + } + + public int getFirstRendered() { + return firstRendered; + } + + public void moveCol(int oldIndex, int newIndex) { + + // loop all rows and move given index to its new place + final Iterator rows = iterator(); + while (rows.hasNext()) { + final VScrollTableRow row = (VScrollTableRow) rows.next(); + + final Element td = DOM.getChild(row.getElement(), oldIndex); + DOM.removeChild(row.getElement(), td); + + DOM.insertChild(row.getElement(), td, newIndex); + + } + + } + + public class VScrollTableRow extends Panel implements ActionOwner, + Container { + + Vector childWidgets = new Vector(); + private boolean selected = false; + private final int rowKey; + private List pendingComponentPaints; + + private String[] actionKeys = null; + private TableRowElement rowElement; + + private VScrollTableRow(int rowKey) { + this.rowKey = rowKey; + rowElement = Document.get().createTRElement(); + setElement(rowElement); + DOM.sinkEvents(getElement(), Event.ONCLICK | Event.ONDBLCLICK + | Event.ONCONTEXTMENU); + } + + private void paintComponent(Paintable p, UIDL uidl) { + if (isAttached()) { + p.updateFromUIDL(uidl, client); + } else { + if (pendingComponentPaints == null) { + pendingComponentPaints = new LinkedList(); + } + pendingComponentPaints.add(uidl); + } + } + + @Override + protected void onAttach() { + super.onAttach(); + if (pendingComponentPaints != null) { + for (UIDL uidl : pendingComponentPaints) { + Paintable paintable = client.getPaintable(uidl); + paintable.updateFromUIDL(uidl, client); + } + } + } + + public String getKey() { + return String.valueOf(rowKey); + } + + public VScrollTableRow(UIDL uidl, char[] aligns) { + this(uidl.getIntAttribute("key")); + + String rowStyle = uidl.getStringAttribute("rowstyle"); + if (rowStyle != null) { + addStyleName(CLASSNAME + "-row-" + rowStyle); + } + + tHead.getColumnAlignments(); + int col = 0; + int visibleColumnIndex = -1; + + // row header + if (showRowHeaders) { + addCell(buildCaptionHtmlSnippet(uidl), aligns[col++], "", + true); + } + + if (uidl.hasAttribute("al")) { + actionKeys = uidl.getStringArrayAttribute("al"); + } + + final Iterator cells = uidl.getChildIterator(); + while (cells.hasNext()) { + final Object cell = cells.next(); + visibleColumnIndex++; + + String columnId = visibleColOrder[visibleColumnIndex]; + + String style = ""; + if (uidl.hasAttribute("style-" + columnId)) { + style = uidl.getStringAttribute("style-" + columnId); + } + + if (cell instanceof String) { + addCell(cell.toString(), aligns[col++], style, false); + } else { + final Paintable cellContent = client + .getPaintable((UIDL) cell); + + addCell((Widget) cellContent, aligns[col++], style); + paintComponent(cellContent, (UIDL) cell); + } + } + if (uidl.hasAttribute("selected") && !isSelected()) { + toggleSelection(); + } + } + + /** + * Add a dummy row, used for measurements if Table is empty. + */ + public VScrollTableRow() { + this(0); + addStyleName(CLASSNAME + "-row"); + addCell("_", 'b', "", true); + } + + public void addCell(String text, char align, String style, + boolean textIsHTML) { + // String only content is optimized by not using Label widget + final Element td = DOM.createTD(); + final Element container = DOM.createDiv(); + String className = CLASSNAME + "-cell-content"; + if (style != null && !style.equals("")) { + className += " " + CLASSNAME + "-cell-content-" + style; + } + td.setClassName(className); + container.setClassName(CLASSNAME + "-cell-wrapper"); + if (textIsHTML) { + container.setInnerHTML(text); + } else { + container.setInnerText(text); + } + if (align != ALIGN_LEFT) { + switch (align) { + case ALIGN_CENTER: + container.getStyle().setProperty("textAlign", "center"); + break; + case ALIGN_RIGHT: + default: + container.getStyle().setProperty("textAlign", "right"); + break; + } + } + td.appendChild(container); + getElement().appendChild(td); + } + + public void addCell(Widget w, char align, String style) { + final Element td = DOM.createTD(); + final Element container = DOM.createDiv(); + String className = CLASSNAME + "-cell-content"; + if (style != null && !style.equals("")) { + className += " " + CLASSNAME + "-cell-content-" + style; + } + td.setClassName(className); + container.setClassName(CLASSNAME + "-cell-wrapper"); + // TODO most components work with this, but not all (e.g. + // Select) + // Old comment: make widget cells respect align. + // text-align:center for IE, margin: auto for others + if (align != ALIGN_LEFT) { + switch (align) { + case ALIGN_CENTER: + container.getStyle().setProperty("textAlign", "center"); + break; + case ALIGN_RIGHT: + default: + container.getStyle().setProperty("textAlign", "right"); + break; + } + } + td.appendChild(container); + getElement().appendChild(td); + // ensure widget not attached to another element (possible tBody + // change) + w.removeFromParent(); + container.appendChild(w.getElement()); + adopt(w); + childWidgets.add(w); + } + + public Iterator iterator() { + return childWidgets.iterator(); + } + + @Override + public boolean remove(Widget w) { + if (childWidgets.contains(w)) { + orphan(w); + DOM.removeChild(DOM.getParent(w.getElement()), w + .getElement()); + childWidgets.remove(w); + return true; + } else { + return false; + } + } + + private void handleClickEvent(Event event, Element targetTdOrTr) { + if (emitClickEvents) { + boolean doubleClick = (DOM.eventGetType(event) == Event.ONDBLCLICK); + + /* This row was clicked */ + client.updateVariable(paintableId, "clickedKey", "" + + rowKey, false); + + if (getElement() == targetTdOrTr.getParentElement()) { + /* A specific column was clicked */ + int childIndex = DOM.getChildIndex(getElement(), + targetTdOrTr); + String colKey = null; + colKey = tHead.getHeaderCell(childIndex).getColKey(); + client.updateVariable(paintableId, "clickedColKey", + colKey, false); + } + + MouseEventDetails details = new MouseEventDetails(event); + // Note: the 'immediate' logic would need to be more + // involved (see #2104), but iscrolltable always sends + // select event, even though nullselectionallowed wont let + // the change trough. Will need to be updated if that is + // changed. + client + .updateVariable( + paintableId, + "clickEvent", + details.toString(), + !(!doubleClick + && selectMode > Table.SELECT_MODE_NONE && immediate)); + } + } + + /* + * React on click that occur on content cells only + */ + @Override + public void onBrowserEvent(Event event) { + if (enabled) { + Element targetTdOrTr = getEventTargetTdOrTr(event); + if (targetTdOrTr != null) { + switch (DOM.eventGetType(event)) { + case Event.ONCLICK: + handleClickEvent(event, targetTdOrTr); + if (selectMode > Table.SELECT_MODE_NONE) { + toggleSelection(); + // Note: changing the immediateness of this + // might + // require changes to "clickEvent" immediateness + // also. + client.updateVariable(paintableId, "selected", + selectedRowKeys.toArray(), immediate); + } + break; + case Event.ONDBLCLICK: + handleClickEvent(event, targetTdOrTr); + break; + case Event.ONCONTEXTMENU: + showContextMenu(event); + break; + default: + break; + } + } + } + super.onBrowserEvent(event); + } + + /** + * Finds the TD that the event interacts with. Returns null if the + * target of the event should not be handled. If the event target is + * the row directly this method returns the TR element instead of + * the TD. + * + * @param event + * @return TD or TR element that the event targets (the actual event + * target is this element or a child of it) + */ + private Element getEventTargetTdOrTr(Event event) { + Element targetTdOrTr = null; + + final Element eventTarget = DOM.eventGetTarget(event); + final Element eventTargetParent = DOM.getParent(eventTarget); + final Element eventTargetGrandParent = DOM + .getParent(eventTargetParent); + + final Element thisTrElement = getElement(); + + if (eventTarget == thisTrElement) { + // This was a click on the TR element + targetTdOrTr = eventTarget; + // rowTarget = true; + } else if (thisTrElement == eventTargetParent) { + // Target parent is the TR, so the actual target is the TD + targetTdOrTr = eventTarget; + } else if (thisTrElement == eventTargetGrandParent) { + // Target grand parent is the TR, so the parent is the TD + targetTdOrTr = eventTargetParent; + } else { + /* + * This is a workaround to make Labels and Embedded in a + * Table clickable (see #2688). It is really not a fix as it + * does not work for a custom component (not extending + * VLabel/VEmbedded) or for read only textfields etc. + */ + Element tdElement = eventTargetParent; + while (DOM.getParent(tdElement) != thisTrElement) { + tdElement = DOM.getParent(tdElement); + } + + Element componentElement = tdElement.getFirstChildElement() + .getFirstChildElement().cast(); + Widget widget = (Widget) client + .getPaintable(componentElement); + if (widget instanceof VLabel || widget instanceof VEmbedded) { + targetTdOrTr = tdElement; + } + } + + return targetTdOrTr; + } + + public void showContextMenu(Event event) { + if (enabled && actionKeys != null) { + int left = event.getClientX(); + int top = event.getClientY(); + top += Window.getScrollTop(); + left += Window.getScrollLeft(); + client.getContextMenu().showAt(this, left, top); + } + event.cancelBubble(true); + event.preventDefault(); + } + + public boolean isSelected() { + return selected; + } + + private void toggleSelection() { + selected = !selected; + if (selected) { + if (selectMode == Table.SELECT_MODE_SINGLE) { + deselectAll(); + } + selectedRowKeys.add(String.valueOf(rowKey)); + addStyleName("i-selected"); + } else { + selectedRowKeys.remove(String.valueOf(rowKey)); + removeStyleName("i-selected"); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.ui.IActionOwner#getActions + * () + */ + public Action[] getActions() { + if (actionKeys == null) { + return new Action[] {}; + } + final Action[] actions = new Action[actionKeys.length]; + for (int i = 0; i < actions.length; i++) { + final String actionKey = actionKeys[i]; + final TreeAction a = new TreeAction(this, String + .valueOf(rowKey), actionKey); + a.setCaption(getActionCaption(actionKey)); + a.setIconUrl(getActionIcon(actionKey)); + actions[i] = a; + } + return actions; + } + + public ApplicationConnection getClient() { + return client; + } + + public String getPaintableId() { + return paintableId; + } + + public RenderSpace getAllocatedSpace(Widget child) { + int w = 0; + int i = getColIndexOf(child); + HeaderCell headerCell = tHead.getHeaderCell(i); + if (headerCell != null) { + if (initializedAndAttached) { + w = headerCell.getWidth(); + } else { + // header offset width is not absolutely correct value, + // but a best guess (expecting similar content in all + // columns -> + // if one component is relative width so are others) + w = headerCell.getOffsetWidth() - getCellExtraWidth(); + } + } + return new RenderSpace(w, getRowHeight()); + } + + private int getColIndexOf(Widget child) { + com.google.gwt.dom.client.Element widgetCell = child + .getElement().getParentElement().getParentElement(); + NodeList cells = rowElement.getCells(); + for (int i = 0; i < cells.getLength(); i++) { + if (cells.getItem(i) == widgetCell) { + return i; + } + } + return -1; + } + + public boolean hasChildComponent(Widget component) { + return childWidgets.contains(component); + } + + public void replaceChildComponent(Widget oldComponent, + Widget newComponent) { + com.google.gwt.dom.client.Element parentElement = oldComponent + .getElement().getParentElement(); + int index = childWidgets.indexOf(oldComponent); + oldComponent.removeFromParent(); + + parentElement.appendChild(newComponent.getElement()); + childWidgets.insertElementAt(newComponent, index); + adopt(newComponent); + + } + + public boolean requestLayout(Set children) { + // row size should never change and system wouldn't event + // survive as this is a kind of fake paitable + return true; + } + + public void updateCaption(Paintable component, UIDL uidl) { + // NOP, not rendered + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + // Should never be called, + // Component container interface faked here to get layouts + // render properly + } + } + } + + public void deselectAll() { + final Object[] keys = selectedRowKeys.toArray(); + for (int i = 0; i < keys.length; i++) { + final VScrollTableRow row = getRenderedRowByKey((String) keys[i]); + if (row != null && row.isSelected()) { + row.toggleSelection(); + } + } + // still ensure all selects are removed from (not necessary rendered) + selectedRowKeys.clear(); + + } + + @Override + public void setWidth(String width) { + if (this.width.equals(width)) { + return; + } + + this.width = width; + if (width != null && !"".equals(width)) { + super.setWidth(width); + int innerPixels = getOffsetWidth() - getBorderWidth(); + if (innerPixels < 0) { + innerPixels = 0; + } + setContentWidth(innerPixels); + + if (!rendering) { + // readjust undefined width columns + lazyAdjustColumnWidths.cancel(); + lazyAdjustColumnWidths.schedule(LAZY_COLUMN_ADJUST_TIMEOUT); + } + + } else { + super.setWidth(""); + } + + } + + private static final int LAZY_COLUMN_ADJUST_TIMEOUT = 300; + + private final Timer lazyAdjustColumnWidths = new Timer() { + /** + * Check for column widths, and available width, to see if we can fix + * column widths "optimally". Doing this lazily to avoid expensive + * calculation when resizing is not yet finished. + */ + @Override + public void run() { + + Iterator headCells = tHead.iterator(); + int usedMinimumWidth = 0; + int totalExplicitColumnsWidths = 0; + float expandRatioDivider = 0; + int colIndex = 0; + while (headCells.hasNext()) { + final HeaderCell hCell = (HeaderCell) headCells.next(); + if (hCell.isDefinedWidth()) { + totalExplicitColumnsWidths += hCell.getWidth(); + usedMinimumWidth += hCell.getWidth(); + } else { + usedMinimumWidth += hCell.getNaturalColumnWidth(colIndex); + expandRatioDivider += hCell.getExpandRatio(); + } + colIndex++; + } + + int availW = tBody.getAvailableWidth(); + // Hey IE, are you really sure about this? + availW = tBody.getAvailableWidth(); + availW -= tBody.getCellExtraWidth() * visibleColOrder.length; + if (willHaveScrollbars()) { + availW -= Util.getNativeScrollbarSize(); + } + + int extraSpace = availW - usedMinimumWidth; + if (extraSpace < 0) { + extraSpace = 0; + } + + int totalUndefinedNaturaWidths = usedMinimumWidth + - totalExplicitColumnsWidths; + + // we have some space that can be divided optimally + HeaderCell hCell; + colIndex = 0; + headCells = tHead.iterator(); + while (headCells.hasNext()) { + hCell = (HeaderCell) headCells.next(); + if (!hCell.isDefinedWidth()) { + int w = hCell.getNaturalColumnWidth(colIndex); + int newSpace; + if (expandRatioDivider > 0) { + // divide excess space by expand ratios + newSpace = (int) (w + extraSpace + * hCell.getExpandRatio() / expandRatioDivider); + } else { + if (totalUndefinedNaturaWidths != 0) { + // divide relatively to natural column widths + newSpace = w + extraSpace * w + / totalUndefinedNaturaWidths; + } else { + newSpace = w; + } + } + setColWidth(colIndex, newSpace, false); + } + colIndex++; + } + Util.runWebkitOverflowAutoFix(bodyContainer.getElement()); + tBody.reLayoutComponents(); + } + }; + + /** + * helper to set pixel size of head and body part + * + * @param pixels + */ + private void setContentWidth(int pixels) { + tHead.setWidth(pixels + "px"); + bodyContainer.setWidth(pixels + "px"); + } + + private int borderWidth = -1; + + /** + * @return border left + border right + */ + private int getBorderWidth() { + if (borderWidth < 0) { + borderWidth = Util.measureHorizontalPaddingAndBorder(bodyContainer + .getElement(), 2); + if (borderWidth < 0) { + borderWidth = 0; + } + } + return borderWidth; + } + + /** + * Ensures scrollable area is properly sized. + */ + private void setContainerHeight() { + if (height != null && !"".equals(height)) { + int contentH = getOffsetHeight() - tHead.getOffsetHeight(); + contentH -= getContentAreaBorderHeight(); + if (contentH < 0) { + contentH = 0; + } + bodyContainer.setHeight(contentH + "px"); + } + } + + private int contentAreaBorderHeight = -1; + + /** + * @return border top + border bottom of the scrollable area of table + */ + private int getContentAreaBorderHeight() { + if (contentAreaBorderHeight < 0) { + DOM.setStyleAttribute(bodyContainer.getElement(), "overflow", + "hidden"); + contentAreaBorderHeight = bodyContainer.getOffsetHeight() + - bodyContainer.getElement().getPropertyInt("clientHeight"); + DOM.setStyleAttribute(bodyContainer.getElement(), "overflow", + "auto"); + } + return contentAreaBorderHeight; + } + + @Override + public void setHeight(String height) { + this.height = height; + super.setHeight(height); + setContainerHeight(); + } + + /* + * Overridden due Table might not survive of visibility change (scroll pos + * lost). Example ITabPanel just set contained components invisible and back + * when changing tabs. + */ + @Override + public void setVisible(boolean visible) { + if (isVisible() != visible) { + super.setVisible(visible); + if (initializedAndAttached) { + if (visible) { + DeferredCommand.addCommand(new Command() { + public void execute() { + bodyContainer.setScrollPosition(firstRowInViewPort + * tBody.getRowHeight()); + } + }); + } + } + } + } + + /** + * Helper function to build html snippet for column or row headers + * + * @param uidl + * possibly with values caption and icon + * @return html snippet containing possibly an icon + caption text + */ + private String buildCaptionHtmlSnippet(UIDL uidl) { + String s = uidl.getStringAttribute("caption"); + if (uidl.hasAttribute("icon")) { + s = "\"icon\"" + s; + } + return s; + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VSlider.java b/src/com/vaadin/terminal/gwt/client/ui/VSlider.java new file mode 100644 index 0000000000..a54e22b34c --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VSlider.java @@ -0,0 +1,436 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ +// +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.DeferredCommand; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.ContainerResizedListener; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; + +public class VSlider extends Widget implements Paintable, Field, + ContainerResizedListener { + + public static final String CLASSNAME = "i-slider"; + + /** + * Minimum size (width or height, depending on orientation) of the slider + * base. + */ + private static final int MIN_SIZE = 50; + + ApplicationConnection client; + + String id; + + private boolean immediate; + private boolean disabled; + private boolean readonly; + private boolean scrollbarStyle; + + private int handleSize; + private double min; + private double max; + private int resolution; + private Double value; + private boolean vertical; + private int size = -1; + private boolean arrows; + + /* DOM element for slider's base */ + private final Element base; + private final int BASE_BORDER_WIDTH = 1; + + /* DOM element for slider's handle */ + private final Element handle; + + /* DOM element for decrement arrow */ + private final Element smaller; + + /* DOM element for increment arrow */ + private final Element bigger; + + /* Temporary dragging/animation variables */ + private boolean dragging = false; + + public VSlider() { + super(); + + setElement(DOM.createDiv()); + base = DOM.createDiv(); + handle = DOM.createDiv(); + smaller = DOM.createDiv(); + bigger = DOM.createDiv(); + + setStyleName(CLASSNAME); + DOM.setElementProperty(base, "className", CLASSNAME + "-base"); + DOM.setElementProperty(handle, "className", CLASSNAME + "-handle"); + DOM.setElementProperty(smaller, "className", CLASSNAME + "-smaller"); + DOM.setElementProperty(bigger, "className", CLASSNAME + "-bigger"); + + DOM.appendChild(getElement(), bigger); + DOM.appendChild(getElement(), smaller); + DOM.appendChild(getElement(), base); + DOM.appendChild(base, handle); + + // Hide initially + DOM.setStyleAttribute(smaller, "display", "none"); + DOM.setStyleAttribute(bigger, "display", "none"); + DOM.setStyleAttribute(handle, "visibility", "hidden"); + + DOM.sinkEvents(getElement(), Event.MOUSEEVENTS | Event.ONMOUSEWHEEL); + DOM.sinkEvents(base, Event.ONCLICK); + DOM.sinkEvents(handle, Event.MOUSEEVENTS); + DOM.sinkEvents(smaller, Event.ONMOUSEDOWN | Event.ONMOUSEUP + | Event.ONMOUSEOUT); + DOM.sinkEvents(bigger, Event.ONMOUSEDOWN | Event.ONMOUSEUP + | Event.ONMOUSEOUT); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + + this.client = client; + id = uidl.getId(); + + // Ensure correct implementation + if (client.updateComponent(this, uidl, true)) { + return; + } + + immediate = uidl.getBooleanAttribute("immediate"); + disabled = uidl.getBooleanAttribute("disabled"); + readonly = uidl.getBooleanAttribute("readonly"); + + vertical = uidl.hasAttribute("vertical"); + arrows = uidl.hasAttribute("arrows"); + + String style = ""; + if (uidl.hasAttribute("style")) { + style = uidl.getStringAttribute("style"); + } + + scrollbarStyle = style.indexOf("scrollbar") > -1; + + if (arrows) { + DOM.setStyleAttribute(smaller, "display", "block"); + DOM.setStyleAttribute(bigger, "display", "block"); + } + + if (vertical) { + addStyleName(CLASSNAME + "-vertical"); + } else { + removeStyleName(CLASSNAME + "-vertical"); + } + + min = uidl.getDoubleAttribute("min"); + max = uidl.getDoubleAttribute("max"); + resolution = uidl.getIntAttribute("resolution"); + value = new Double(uidl.getDoubleVariable("value")); + + handleSize = uidl.getIntAttribute("hsize"); + + buildBase(); + + if (!vertical) { + // Draw handle with a delay to allow base to gain maximum width + DeferredCommand.addCommand(new Command() { + public void execute() { + buildHandle(); + setValue(value, false); + } + }); + } else { + buildHandle(); + setValue(value, false); + } + } + + private void buildBase() { + final String styleAttribute = vertical ? "height" : "width"; + final String domProperty = vertical ? "offsetHeight" : "offsetWidth"; + + if (size == -1) { + final Element p = DOM.getParent(getElement()); + if (DOM.getElementPropertyInt(p, domProperty) > 50) { + if (vertical) { + setHeight(); + } else { + DOM.setStyleAttribute(base, styleAttribute, ""); + } + } else { + // Set minimum size and adjust after all components have + // (supposedly) been drawn completely. + DOM.setStyleAttribute(base, styleAttribute, MIN_SIZE + "px"); + DeferredCommand.addCommand(new Command() { + public void execute() { + final Element p = DOM.getParent(getElement()); + if (DOM.getElementPropertyInt(p, domProperty) > (MIN_SIZE + 5)) { + if (vertical) { + setHeight(); + } else { + DOM.setStyleAttribute(base, styleAttribute, ""); + } + // Ensure correct position + setValue(value, false); + } + } + }); + } + } else { + DOM.setStyleAttribute(base, styleAttribute, size + "px"); + } + + // TODO attach listeners for focusing and arrow keys + } + + private void buildHandle() { + final String styleAttribute = vertical ? "height" : "width"; + final String handleAttribute = vertical ? "marginTop" : "marginLeft"; + final String domProperty = vertical ? "offsetHeight" : "offsetWidth"; + + DOM.setStyleAttribute(handle, handleAttribute, "0"); + + if (scrollbarStyle) { + // Only stretch the handle if scrollbar style is set. + int s = (int) (Double.parseDouble(DOM.getElementProperty(base, + domProperty)) / 100 * handleSize); + if (handleSize == -1) { + final int baseS = Integer.parseInt(DOM.getElementProperty(base, + domProperty)); + final double range = (max - min) * (resolution + 1) * 3; + s = (int) (baseS - range); + } + if (s < 3) { + s = 3; + } + DOM.setStyleAttribute(handle, styleAttribute, s + "px"); + } else { + DOM.setStyleAttribute(handle, styleAttribute, ""); + } + + // Restore visibility + DOM.setStyleAttribute(handle, "visibility", "visible"); + + } + + private void setValue(Double value, boolean updateToServer) { + if (value == null) { + return; + } + + if (value.doubleValue() < min) { + value = new Double(min); + } else if (value.doubleValue() > max) { + value = new Double(max); + } + + // Update handle position + final String styleAttribute = vertical ? "marginTop" : "marginLeft"; + final String domProperty = vertical ? "offsetHeight" : "offsetWidth"; + final int handleSize = Integer.parseInt(DOM.getElementProperty(handle, + domProperty)); + final int baseSize = Integer.parseInt(DOM.getElementProperty(base, + domProperty)) + - (2 * BASE_BORDER_WIDTH); + + final int range = baseSize - handleSize; + double v = value.doubleValue(); + // Round value to resolution + if (resolution > 0) { + v = Math.round(v * Math.pow(10, resolution)); + v = v / Math.pow(10, resolution); + } else { + v = Math.round(v); + } + final double valueRange = max - min; + double p = 0; + if (valueRange > 0) { + p = range * ((v - min) / valueRange); + } + if (p < 0) { + p = 0; + } + if (vertical) { + // IE6 rounding behaves a little unstable, reduce one pixel so the + // containing element (base) won't expand without limits + p = range - p - (BrowserInfo.get().isIE6() ? 1 : 0); + } + final double pos = p; + + DOM.setStyleAttribute(handle, styleAttribute, (Math.round(pos)) + "px"); + + // TODO give more detailed info when dragging and do roundup + DOM.setElementAttribute(handle, "title", "" + v); + + // Update value + this.value = new Double(v); + + if (updateToServer) { + updateValueToServer(); + } + } + + @Override + public void onBrowserEvent(Event event) { + if (disabled || readonly) { + return; + } + final Element targ = DOM.eventGetTarget(event); + + if (DOM.eventGetType(event) == Event.ONMOUSEWHEEL) { + processMouseWheelEvent(event); + } else if (dragging || targ == handle) { + processHandleEvent(event); + } else if (targ == smaller) { + decreaseValue(true); + } else if (targ == bigger) { + increaseValue(true); + } else { + processBaseEvent(event); + } + } + + private Timer scrollTimer; + + private void processMouseWheelEvent(final Event event) { + final int dir = DOM.eventGetMouseWheelVelocityY(event); + + if (dir < 0) { + increaseValue(false); + } else { + decreaseValue(false); + } + + if (scrollTimer != null) { + scrollTimer.cancel(); + } + scrollTimer = new Timer() { + @Override + public void run() { + updateValueToServer(); + } + }; + scrollTimer.schedule(100); + + DOM.eventPreventDefault(event); + DOM.eventCancelBubble(event, true); + } + + private void processHandleEvent(Event event) { + switch (DOM.eventGetType(event)) { + case Event.ONMOUSEDOWN: + if (!disabled && !readonly) { + dragging = true; + DOM.setCapture(getElement()); + DOM.eventPreventDefault(event); // prevent selecting text + DOM.eventCancelBubble(event, true); + } + break; + case Event.ONMOUSEMOVE: + if (dragging) { + // DOM.setCapture(getElement()); + setValueByEvent(event, false); + } + break; + case Event.ONMOUSEUP: + dragging = false; + DOM.releaseCapture(getElement()); + setValueByEvent(event, true); + break; + default: + break; + } + } + + private void processBaseEvent(Event event) { + if (DOM.eventGetType(event) == Event.ONMOUSEDOWN) { + if (!disabled && !readonly && !dragging) { + setValueByEvent(event, true); + DOM.eventCancelBubble(event, true); + } + } else if (DOM.eventGetType(event) == Event.ONMOUSEDOWN && dragging) { + dragging = false; + DOM.releaseCapture(getElement()); + setValueByEvent(event, true); + } + } + + private void decreaseValue(boolean updateToServer) { + setValue(new Double(value.doubleValue() - Math.pow(10, -resolution)), + updateToServer); + } + + private void increaseValue(boolean updateToServer) { + setValue(new Double(value.doubleValue() + Math.pow(10, -resolution)), + updateToServer); + } + + private void setValueByEvent(Event event, boolean updateToServer) { + double v = min; // Fallback to min + + final int coord = vertical ? DOM.eventGetClientY(event) : DOM + .eventGetClientX(event); + final String domProperty = vertical ? "offsetHeight" : "offsetWidth"; + + final double handleSize = Integer.parseInt(DOM.getElementProperty( + handle, domProperty)); + final double baseSize = Integer.parseInt(DOM.getElementProperty(base, + domProperty)); + final double baseOffset = vertical ? DOM.getAbsoluteTop(base) + - handleSize / 2 : DOM.getAbsoluteLeft(base) + handleSize / 2; + + if (vertical) { + v = ((baseSize - (coord - baseOffset)) / (baseSize - handleSize)) + * (max - min) + min; + } else { + v = ((coord - baseOffset) / (baseSize - handleSize)) * (max - min) + + min; + } + + if (v < min) { + v = min; + } else if (v > max) { + v = max; + } + + setValue(new Double(v), updateToServer); + } + + public void iLayout() { + if (vertical) { + setHeight(); + } + // Update handle position + setValue(value, false); + } + + private void setHeight() { + if (size == -1) { + // Calculate decoration size + DOM.setStyleAttribute(base, "height", "0"); + DOM.setStyleAttribute(base, "overflow", "hidden"); + int h = DOM.getElementPropertyInt(getElement(), "offsetHeight"); + if (h < MIN_SIZE) { + h = MIN_SIZE; + } + DOM.setStyleAttribute(base, "height", h + "px"); + } else { + DOM.setStyleAttribute(base, "height", size + "px"); + } + DOM.setStyleAttribute(base, "overflow", ""); + } + + private void updateValueToServer() { + client.updateVariable(id, "value", value.doubleValue(), immediate); + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VSplitPanel.java b/src/com/vaadin/terminal/gwt/client/ui/VSplitPanel.java new file mode 100644 index 0000000000..f359156c72 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VSplitPanel.java @@ -0,0 +1,584 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.Set; + +import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.DeferredCommand; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.ComplexPanel; +import com.google.gwt.user.client.ui.RootPanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.Container; +import com.vaadin.terminal.gwt.client.ContainerResizedListener; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.RenderInformation; +import com.vaadin.terminal.gwt.client.RenderSpace; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +public class VSplitPanel extends ComplexPanel implements Container, + ContainerResizedListener { + public static final String CLASSNAME = "i-splitpanel"; + + public static final int ORIENTATION_HORIZONTAL = 0; + + public static final int ORIENTATION_VERTICAL = 1; + + private static final int MIN_SIZE = 30; + + private int orientation = ORIENTATION_HORIZONTAL; + + private Widget firstChild; + + private Widget secondChild; + + private final Element wrapper = DOM.createDiv(); + + private final Element firstContainer = DOM.createDiv(); + + private final Element secondContainer = DOM.createDiv(); + + private final Element splitter = DOM.createDiv(); + + private boolean resizing; + + private int origX; + + private int origY; + + private int origMouseX; + + private int origMouseY; + + private boolean locked = false; + + private String[] componentStyleNames; + + private Element draggingCurtain; + + private ApplicationConnection client; + + private String width = ""; + + private String height = ""; + + private RenderSpace firstRenderSpace = new RenderSpace(0, 0, true); + private RenderSpace secondRenderSpace = new RenderSpace(0, 0, true); + + RenderInformation renderInformation = new RenderInformation(); + + private String id; + + private boolean immediate; + + private boolean rendering = false; + + public VSplitPanel() { + this(ORIENTATION_HORIZONTAL); + } + + public VSplitPanel(int orientation) { + setElement(DOM.createDiv()); + switch (orientation) { + case ORIENTATION_HORIZONTAL: + setStyleName(CLASSNAME + "-horizontal"); + break; + case ORIENTATION_VERTICAL: + default: + setStyleName(CLASSNAME + "-vertical"); + break; + } + // size below will be overridden in update from uidl, initial size + // needed to keep IE alive + setWidth(MIN_SIZE + "px"); + setHeight(MIN_SIZE + "px"); + constructDom(); + setOrientation(orientation); + DOM.sinkEvents(splitter, (Event.MOUSEEVENTS)); + DOM.sinkEvents(getElement(), (Event.MOUSEEVENTS)); + } + + protected void constructDom() { + DOM.appendChild(splitter, DOM.createDiv()); // for styling + DOM.appendChild(getElement(), wrapper); + DOM.setStyleAttribute(wrapper, "position", "relative"); + DOM.setStyleAttribute(wrapper, "width", "100%"); + DOM.setStyleAttribute(wrapper, "height", "100%"); + + DOM.appendChild(wrapper, secondContainer); + DOM.appendChild(wrapper, firstContainer); + DOM.appendChild(wrapper, splitter); + + DOM.setStyleAttribute(splitter, "position", "absolute"); + DOM.setStyleAttribute(secondContainer, "position", "absolute"); + + DOM.setStyleAttribute(firstContainer, "overflow", "auto"); + DOM.setStyleAttribute(secondContainer, "overflow", "auto"); + + } + + private void setOrientation(int orientation) { + this.orientation = orientation; + if (orientation == ORIENTATION_HORIZONTAL) { + DOM.setStyleAttribute(splitter, "height", "100%"); + DOM.setStyleAttribute(splitter, "top", "0"); + DOM.setStyleAttribute(firstContainer, "height", "100%"); + DOM.setStyleAttribute(secondContainer, "height", "100%"); + } else { + DOM.setStyleAttribute(splitter, "width", "100%"); + DOM.setStyleAttribute(splitter, "left", "0"); + DOM.setStyleAttribute(firstContainer, "width", "100%"); + DOM.setStyleAttribute(secondContainer, "width", "100%"); + } + + DOM.setElementProperty(firstContainer, "className", CLASSNAME + + "-first-container"); + DOM.setElementProperty(secondContainer, "className", CLASSNAME + + "-second-container"); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + this.client = client; + id = uidl.getId(); + rendering = true; + + immediate = uidl.hasAttribute("immediate"); + + if (client.updateComponent(this, uidl, true)) { + rendering = false; + return; + } + + if (uidl.hasAttribute("style")) { + componentStyleNames = uidl.getStringAttribute("style").split(" "); + } else { + componentStyleNames = new String[0]; + } + + setLocked(uidl.getBooleanAttribute("locked")); + + setStylenames(); + + setSplitPosition(uidl.getStringAttribute("position")); + + final Paintable newFirstChild = client.getPaintable(uidl + .getChildUIDL(0)); + final Paintable newSecondChild = client.getPaintable(uidl + .getChildUIDL(1)); + if (firstChild != newFirstChild) { + if (firstChild != null) { + client.unregisterPaintable((Paintable) firstChild); + } + setFirstWidget((Widget) newFirstChild); + } + if (secondChild != newSecondChild) { + if (secondChild != null) { + client.unregisterPaintable((Paintable) secondChild); + } + setSecondWidget((Widget) newSecondChild); + } + newFirstChild.updateFromUIDL(uidl.getChildUIDL(0), client); + newSecondChild.updateFromUIDL(uidl.getChildUIDL(1), client); + + renderInformation.updateSize(getElement()); + + if (BrowserInfo.get().isIE7()) { + // Part III of IE7 hack + DeferredCommand.addCommand(new Command() { + public void execute() { + iLayout(); + } + }); + } + rendering = false; + + } + + private void setLocked(boolean newValue) { + if (locked != newValue) { + locked = newValue; + splitterSize = -1; + setStylenames(); + } + } + + private void setSplitPosition(String pos) { + if (orientation == ORIENTATION_HORIZONTAL) { + DOM.setStyleAttribute(splitter, "left", pos); + } else { + DOM.setStyleAttribute(splitter, "top", pos); + } + iLayout(); + client.runDescendentsLayout(this); + + } + + /* + * Calculates absolutely positioned container places/sizes (non-Javadoc) + * + * @see com.vaadin.terminal.gwt.client.NeedsLayout#layout() + */ + public void iLayout() { + if (!isAttached()) { + return; + } + + renderInformation.updateSize(getElement()); + + int wholeSize; + int pixelPosition; + + switch (orientation) { + case ORIENTATION_HORIZONTAL: + wholeSize = DOM.getElementPropertyInt(wrapper, "clientWidth"); + pixelPosition = DOM.getElementPropertyInt(splitter, "offsetLeft"); + + // reposition splitter in case it is out of box + if (pixelPosition > 0 + && pixelPosition + getSplitterSize() > wholeSize) { + pixelPosition = wholeSize - getSplitterSize(); + if (pixelPosition < 0) { + pixelPosition = 0; + } + setSplitPosition(pixelPosition + "px"); + return; + } + + DOM + .setStyleAttribute(firstContainer, "width", pixelPosition + + "px"); + int secondContainerWidth = (wholeSize - pixelPosition - getSplitterSize()); + if (secondContainerWidth < 0) { + secondContainerWidth = 0; + } + DOM.setStyleAttribute(secondContainer, "width", + secondContainerWidth + "px"); + DOM.setStyleAttribute(secondContainer, "left", + (pixelPosition + getSplitterSize()) + "px"); + + int contentHeight = renderInformation.getRenderedSize().getHeight(); + firstRenderSpace.setHeight(contentHeight); + firstRenderSpace.setWidth(pixelPosition); + secondRenderSpace.setHeight(contentHeight); + secondRenderSpace.setWidth(secondContainerWidth); + + break; + case ORIENTATION_VERTICAL: + wholeSize = DOM.getElementPropertyInt(wrapper, "clientHeight"); + pixelPosition = DOM.getElementPropertyInt(splitter, "offsetTop"); + + // reposition splitter in case it is out of box + if (pixelPosition > 0 + && pixelPosition + getSplitterSize() > wholeSize) { + pixelPosition = wholeSize - getSplitterSize(); + if (pixelPosition < 0) { + pixelPosition = 0; + } + setSplitPosition(pixelPosition + "px"); + return; + } + + DOM.setStyleAttribute(firstContainer, "height", pixelPosition + + "px"); + int secondContainerHeight = (wholeSize - pixelPosition - getSplitterSize()); + if (secondContainerHeight < 0) { + secondContainerHeight = 0; + } + DOM.setStyleAttribute(secondContainer, "height", + secondContainerHeight + "px"); + DOM.setStyleAttribute(secondContainer, "top", + (pixelPosition + getSplitterSize()) + "px"); + + int contentWidth = renderInformation.getRenderedSize().getWidth(); + firstRenderSpace.setHeight(pixelPosition); + firstRenderSpace.setWidth(contentWidth); + secondRenderSpace.setHeight(secondContainerHeight); + secondRenderSpace.setWidth(contentWidth); + + break; + } + + // fixes scrollbars issues on webkit based browsers + Util.runWebkitOverflowAutoFix(secondContainer); + Util.runWebkitOverflowAutoFix(firstContainer); + + } + + private void setFirstWidget(Widget w) { + if (firstChild != null) { + firstChild.removeFromParent(); + } + super.add(w, firstContainer); + firstChild = w; + } + + private void setSecondWidget(Widget w) { + if (secondChild != null) { + secondChild.removeFromParent(); + } + super.add(w, secondContainer); + secondChild = w; + } + + @Override + public void onBrowserEvent(Event event) { + switch (DOM.eventGetType(event)) { + case Event.ONMOUSEMOVE: + if (resizing) { + onMouseMove(event); + } + break; + case Event.ONMOUSEDOWN: + onMouseDown(event); + break; + case Event.ONMOUSEUP: + if (resizing) { + onMouseUp(event); + } + break; + case Event.ONCLICK: + resizing = false; + break; + } + } + + public void onMouseDown(Event event) { + if (locked) { + return; + } + final Element trg = DOM.eventGetTarget(event); + if (trg == splitter || trg == DOM.getChild(splitter, 0)) { + resizing = true; + if (BrowserInfo.get().isGecko()) { + showDraggingCurtain(); + } + DOM.setCapture(getElement()); + origX = DOM.getElementPropertyInt(splitter, "offsetLeft"); + origY = DOM.getElementPropertyInt(splitter, "offsetTop"); + origMouseX = DOM.eventGetClientX(event); + origMouseY = DOM.eventGetClientY(event); + DOM.eventCancelBubble(event, true); + DOM.eventPreventDefault(event); + } + } + + public void onMouseMove(Event event) { + switch (orientation) { + case ORIENTATION_HORIZONTAL: + final int x = DOM.eventGetClientX(event); + onHorizontalMouseMove(x); + break; + case ORIENTATION_VERTICAL: + default: + final int y = DOM.eventGetClientY(event); + onVerticalMouseMove(y); + break; + } + iLayout(); + // TODO Check if this is needed + client.runDescendentsLayout(this); + + } + + private void onHorizontalMouseMove(int x) { + int newX = origX + x - origMouseX; + if (newX < 0) { + newX = 0; + } + if (newX + getSplitterSize() > getOffsetWidth()) { + newX = getOffsetWidth() - getSplitterSize(); + } + DOM.setStyleAttribute(splitter, "left", newX + "px"); + updateSplitPosition(newX); + } + + private void onVerticalMouseMove(int y) { + int newY = origY + y - origMouseY; + if (newY < 0) { + newY = 0; + } + + if (newY + getSplitterSize() > getOffsetHeight()) { + newY = getOffsetHeight() - getSplitterSize(); + } + DOM.setStyleAttribute(splitter, "top", newY + "px"); + updateSplitPosition(newY); + } + + public void onMouseUp(Event event) { + DOM.releaseCapture(getElement()); + if (BrowserInfo.get().isGecko()) { + hideDraggingCurtain(); + } + resizing = false; + onMouseMove(event); + } + + /** + * Used in FF to avoid losing mouse capture when pointer is moved on an + * iframe. + */ + private void showDraggingCurtain() { + if (draggingCurtain == null) { + draggingCurtain = DOM.createDiv(); + DOM.setStyleAttribute(draggingCurtain, "position", "absolute"); + DOM.setStyleAttribute(draggingCurtain, "top", "0px"); + DOM.setStyleAttribute(draggingCurtain, "left", "0px"); + DOM.setStyleAttribute(draggingCurtain, "width", "100%"); + DOM.setStyleAttribute(draggingCurtain, "height", "100%"); + DOM.setStyleAttribute(draggingCurtain, "zIndex", "" + + VToolkitOverlay.Z_INDEX); + DOM.appendChild(RootPanel.getBodyElement(), draggingCurtain); + } + } + + /** + * Hides dragging curtain + */ + private void hideDraggingCurtain() { + if (draggingCurtain != null) { + DOM.removeChild(RootPanel.getBodyElement(), draggingCurtain); + draggingCurtain = null; + } + } + + private int splitterSize = -1; + + private int getSplitterSize() { + if (splitterSize < 0) { + if (isAttached()) { + switch (orientation) { + case ORIENTATION_HORIZONTAL: + splitterSize = DOM.getElementPropertyInt(splitter, + "offsetWidth"); + break; + + default: + splitterSize = DOM.getElementPropertyInt(splitter, + "offsetHeight"); + break; + } + } + } + return splitterSize; + } + + @Override + public void setHeight(String height) { + if (this.height.equals(height)) { + return; + } + + this.height = height; + super.setHeight(height); + if (!rendering && client != null) { + iLayout(); + client.runDescendentsLayout(this); + } + } + + @Override + public void setWidth(String width) { + if (this.width.equals(width)) { + return; + } + + this.width = width; + super.setWidth(width); + if (!rendering && client != null) { + iLayout(); + client.runDescendentsLayout(this); + } + } + + public RenderSpace getAllocatedSpace(Widget child) { + if (child == firstChild) { + return firstRenderSpace; + } else if (child == secondChild) { + return secondRenderSpace; + } + + return null; + } + + public boolean hasChildComponent(Widget component) { + return (component != null && (component == firstChild || component == secondChild)); + } + + public void replaceChildComponent(Widget oldComponent, Widget newComponent) { + if (oldComponent == firstChild) { + setFirstWidget(newComponent); + } else if (oldComponent == secondChild) { + setSecondWidget(newComponent); + } + } + + public boolean requestLayout(Set child) { + if (height != null && width != null) { + /* + * If the height and width has been specified the child components + * cannot make the size of the layout change + */ + + return true; + } + + if (renderInformation.updateSize(getElement())) { + return false; + } else { + return true; + } + + } + + public void updateCaption(Paintable component, UIDL uidl) { + // TODO Implement caption handling + } + + /** + * Updates the new split position back to server. + * + * @param pos + * The new position of the split handle. + */ + private void updateSplitPosition(int pos) { + // We always send pixel values to server + client.updateVariable(id, "position", pos, immediate); + } + + private void setStylenames() { + final String splitterSuffix = (orientation == ORIENTATION_HORIZONTAL ? "-hsplitter" + : "-vsplitter"); + final String firstContainerSuffix = "-first-container"; + final String secondContainerSuffix = "-second-container"; + String lockedSuffix = ""; + + String splitterStyle = CLASSNAME + splitterSuffix; + String firstStyle = CLASSNAME + firstContainerSuffix; + String secondStyle = CLASSNAME + secondContainerSuffix; + + if (locked) { + splitterStyle = CLASSNAME + splitterSuffix + "-locked"; + lockedSuffix = "-locked"; + } + for (int i = 0; i < componentStyleNames.length; i++) { + splitterStyle += " " + CLASSNAME + splitterSuffix + "-" + + componentStyleNames[i] + lockedSuffix; + firstStyle += " " + CLASSNAME + firstContainerSuffix + "-" + + componentStyleNames[i]; + secondStyle += " " + CLASSNAME + secondContainerSuffix + "-" + + componentStyleNames[i]; + } + DOM.setElementProperty(splitter, "className", splitterStyle); + DOM.setElementProperty(firstContainer, "className", firstStyle); + DOM.setElementProperty(secondContainer, "className", secondStyle); + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VSplitPanelHorizontal.java b/src/com/vaadin/terminal/gwt/client/ui/VSplitPanelHorizontal.java new file mode 100644 index 0000000000..98002e9fd3 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VSplitPanelHorizontal.java @@ -0,0 +1,12 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +public class VSplitPanelHorizontal extends VSplitPanel { + + public VSplitPanelHorizontal() { + super(VSplitPanel.ORIENTATION_HORIZONTAL); + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VSplitPanelVertical.java b/src/com/vaadin/terminal/gwt/client/ui/VSplitPanelVertical.java new file mode 100644 index 0000000000..4bbf662053 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VSplitPanelVertical.java @@ -0,0 +1,12 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +public class VSplitPanelVertical extends VSplitPanel { + + public VSplitPanelVertical() { + super(VSplitPanel.ORIENTATION_VERTICAL); + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTablePaging.java b/src/com/vaadin/terminal/gwt/client/ui/VTablePaging.java new file mode 100644 index 0000000000..3db1ce87e6 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VTablePaging.java @@ -0,0 +1,439 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Set; +import java.util.Vector; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.ClickListener; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.Grid; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; + +/** + * TODO make this work (just an early prototype). We may want to have paging + * style table which will be much lighter than VScrollTable is. + */ +public class VTablePaging extends Composite implements Table, Paintable, + ClickListener { + + private final Grid tBody = new Grid(); + private final Button nextPage = new Button(">"); + private final Button prevPage = new Button("<"); + private final Button firstPage = new Button("<<"); + private final Button lastPage = new Button(">>"); + + private int pageLength = 15; + + private boolean rowHeaders = false; + + private ApplicationConnection client; + private String id; + + private boolean immediate = false; + + private int selectMode = Table.SELECT_MODE_NONE; + + private final Vector selectedRowKeys = new Vector(); + + private int totalRows; + + private final HashMap visibleColumns = new HashMap(); + + private int rows; + + private int firstRow; + private boolean sortAscending = true; + private final HorizontalPanel pager; + + public HashMap rowKeysToTableRows = new HashMap(); + + public VTablePaging() { + + tBody.setStyleName("itable-tbody"); + + final VerticalPanel panel = new VerticalPanel(); + + pager = new HorizontalPanel(); + pager.add(firstPage); + firstPage.addClickListener(this); + pager.add(prevPage); + prevPage.addClickListener(this); + pager.add(nextPage); + nextPage.addClickListener(this); + pager.add(lastPage); + lastPage.addClickListener(this); + + panel.add(pager); + panel.add(tBody); + + initWidget(panel); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + if (client.updateComponent(this, uidl, true)) { + return; + } + + this.client = client; + id = uidl.getStringAttribute("id"); + immediate = uidl.getBooleanAttribute("immediate"); + totalRows = uidl.getIntAttribute("totalrows"); + pageLength = uidl.getIntAttribute("pagelength"); + firstRow = uidl.getIntAttribute("firstrow"); + rows = uidl.getIntAttribute("rows"); + + if (uidl.hasAttribute("selectmode")) { + if (uidl.getStringAttribute("selectmode").equals("multi")) { + selectMode = Table.SELECT_MODE_MULTI; + } else { + selectMode = Table.SELECT_MODE_SINGLE; + } + + if (uidl.hasAttribute("selected")) { + final Set selectedKeys = uidl + .getStringArrayVariableAsSet("selected"); + selectedRowKeys.clear(); + for (final Iterator it = selectedKeys.iterator(); it.hasNext();) { + selectedRowKeys.add(it.next()); + } + } + } + + if (uidl.hasVariable("sortascending")) { + sortAscending = uidl.getBooleanVariable("sortascending"); + } + + if (uidl.hasAttribute("rowheaders")) { + rowHeaders = true; + } + + UIDL rowData = null; + UIDL visibleColumns = null; + for (final Iterator it = uidl.getChildIterator(); it.hasNext();) { + final UIDL c = (UIDL) it.next(); + if (c.getTag().equals("rows")) { + rowData = c; + } else if (c.getTag().equals("actions")) { + updateActionMap(c); + } else if (c.getTag().equals("visiblecolumns")) { + visibleColumns = c; + } + } + tBody.resize(rows + 1, uidl.getIntAttribute("cols") + + (rowHeaders ? 1 : 0)); + updateHeader(visibleColumns); + updateBody(rowData); + + updatePager(); + } + + private void updateHeader(UIDL c) { + final Iterator it = c.getChildIterator(); + visibleColumns.clear(); + int colIndex = (rowHeaders ? 1 : 0); + while (it.hasNext()) { + final UIDL col = (UIDL) it.next(); + final String cid = col.getStringAttribute("cid"); + if (!col.hasAttribute("collapsed")) { + tBody.setWidget(0, colIndex, new HeaderCell(cid, col + .getStringAttribute("caption"))); + + } + colIndex++; + } + } + + private void updateActionMap(UIDL c) { + // TODO Auto-generated method stub + + } + + /** + * Updates row data from uidl. UpdateFromUIDL delegates updating tBody to + * this method. + * + * Updates may be to different part of tBody, depending on update type. It + * can be initial row data, scroll up, scroll down... + * + * @param uidl + * which contains row data + */ + private void updateBody(UIDL uidl) { + final Iterator it = uidl.getChildIterator(); + + int curRowIndex = 1; + while (it.hasNext()) { + final UIDL rowUidl = (UIDL) it.next(); + final TableRow row = new TableRow(curRowIndex, String + .valueOf(rowUidl.getIntAttribute("key")), rowUidl + .hasAttribute("selected")); + int colIndex = 0; + if (rowHeaders) { + tBody.setWidget(curRowIndex, colIndex, new BodyCell(row, + rowUidl.getStringAttribute("caption"))); + colIndex++; + } + final Iterator cells = rowUidl.getChildIterator(); + while (cells.hasNext()) { + final Object cell = cells.next(); + if (cell instanceof String) { + tBody.setWidget(curRowIndex, colIndex, new BodyCell(row, + (String) cell)); + } else { + final Paintable cellContent = client + .getPaintable((UIDL) cell); + final BodyCell bodyCell = new BodyCell(row); + bodyCell.setWidget((Widget) cellContent); + tBody.setWidget(curRowIndex, colIndex, bodyCell); + } + colIndex++; + } + curRowIndex++; + } + } + + private void updatePager() { + if (pageLength == 0) { + pager.setVisible(false); + return; + } + if (isFirstPage()) { + firstPage.setEnabled(false); + prevPage.setEnabled(false); + } else { + firstPage.setEnabled(true); + prevPage.setEnabled(true); + } + if (hasNextPage()) { + nextPage.setEnabled(true); + lastPage.setEnabled(true); + } else { + nextPage.setEnabled(false); + lastPage.setEnabled(false); + + } + } + + private boolean hasNextPage() { + if (firstRow + rows + 1 > totalRows) { + return false; + } + return true; + } + + private boolean isFirstPage() { + if (firstRow == 0) { + return true; + } + return false; + } + + public void onClick(Widget sender) { + if (sender instanceof Button) { + if (sender == firstPage) { + client.updateVariable(id, "firstvisible", 0, true); + } else if (sender == nextPage) { + client.updateVariable(id, "firstvisible", + firstRow + pageLength, true); + } else if (sender == prevPage) { + int newFirst = firstRow - pageLength; + if (newFirst < 0) { + newFirst = 0; + } + client.updateVariable(id, "firstvisible", newFirst, true); + } else if (sender == lastPage) { + client.updateVariable(id, "firstvisible", totalRows + - pageLength, true); + } + } + if (sender instanceof HeaderCell) { + final HeaderCell hCell = (HeaderCell) sender; + client.updateVariable(id, "sortcolumn", hCell.getCid(), false); + client.updateVariable(id, "sortascending", (sortAscending ? false + : true), true); + } + } + + private class HeaderCell extends HTML { + + private String cid; + + public String getCid() { + return cid; + } + + public void setCid(String pid) { + cid = pid; + } + + HeaderCell(String pid, String caption) { + super(); + cid = pid; + addClickListener(VTablePaging.this); + setText(caption); + // TODO remove debug color + DOM.setStyleAttribute(getElement(), "color", "brown"); + DOM.setStyleAttribute(getElement(), "font-weight", "bold"); + } + } + + /** + * Abstraction of table cell content. In needs to know on which row it is in + * case of context click. + * + * @author mattitahvonen + */ + public class BodyCell extends SimplePanel { + private final TableRow row; + + public BodyCell(TableRow row) { + super(); + sinkEvents(Event.BUTTON_LEFT | Event.BUTTON_RIGHT); + this.row = row; + } + + public BodyCell(TableRow row2, String textContent) { + super(); + sinkEvents(Event.BUTTON_LEFT | Event.BUTTON_RIGHT); + row = row2; + setWidget(new Label(textContent)); + } + + @Override + public void onBrowserEvent(Event event) { + System.out.println("CEll event: " + event.toString()); + switch (DOM.eventGetType(event)) { + case Event.BUTTON_RIGHT: + row.showContextMenu(event); + Window.alert("context menu un-implemented"); + DOM.eventCancelBubble(event, true); + break; + case Event.BUTTON_LEFT: + if (selectMode > Table.SELECT_MODE_NONE) { + row.toggleSelected(); + } + break; + default: + break; + } + super.onBrowserEvent(event); + } + } + + private class TableRow { + + private final String key; + private final int rowIndex; + private boolean selected = false; + + public TableRow(int rowIndex, String rowKey, boolean selected) { + rowKeysToTableRows.put(rowKey, this); + this.rowIndex = rowIndex; + key = rowKey; + setSelected(selected); + } + + /** + * This method is used to set row status. Does not change value on + * server. + * + * @param selected + */ + public void setSelected(boolean sel) { + selected = sel; + if (selected) { + selectedRowKeys.add(key); + DOM.setStyleAttribute(tBody.getRowFormatter().getElement( + rowIndex), "background", "yellow"); + + } else { + selectedRowKeys.remove(key); + DOM.setStyleAttribute(tBody.getRowFormatter().getElement( + rowIndex), "background", "transparent"); + } + } + + public void setContextMenuOptions(HashMap options) { + + } + + /** + * Toggles rows select state. Also updates state to server according to + * tables immediate flag. + * + */ + public void toggleSelected() { + if (selected) { + setSelected(false); + } else { + if (selectMode == Table.SELECT_MODE_SINGLE) { + deselectAll(); + } + setSelected(true); + } + client.updateVariable(id, "selected", selectedRowKeys.toArray(), + immediate); + } + + /** + * Shows context menu for this row. + * + * @param event + * Event which triggered context menu. Correct place for + * context menu can be determined with it. + */ + public void showContextMenu(Event event) { + System.out.println("TODO: Show context menu"); + } + } + + public void deselectAll() { + final Object[] keys = selectedRowKeys.toArray(); + for (int i = 0; i < keys.length; i++) { + final TableRow tableRow = (TableRow) rowKeysToTableRows + .get(keys[i]); + if (tableRow != null) { + tableRow.setSelected(false); + } + } + // still ensure all selects are removed from + selectedRowKeys.clear(); + } + + public void add(Widget w) { + // TODO Auto-generated method stub + + } + + public void clear() { + // TODO Auto-generated method stub + + } + + public Iterator iterator() { + // TODO Auto-generated method stub + return null; + } + + public boolean remove(Widget w) { + // TODO Auto-generated method stub + return false; + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTabsheet.java b/src/com/vaadin/terminal/gwt/client/ui/VTabsheet.java new file mode 100644 index 0000000000..4cc2887dd0 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VTabsheet.java @@ -0,0 +1,841 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Set; + +import com.google.gwt.dom.client.Style; +import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.DeferredCommand; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.ClickListener; +import com.google.gwt.user.client.ui.ComplexPanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.VCaption; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.RenderInformation; +import com.vaadin.terminal.gwt.client.RenderSpace; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +public class VTabsheet extends VTabsheetBase { + + private class TabSheetCaption extends VCaption { + TabSheetCaption() { + super(null, client); + } + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + if (event.getTypeInt() == Event.ONLOAD) { + // icon onloads may change total width of tabsheet + if (isDynamicWidth()) { + updateDynamicWidth(); + } + updateTabScroller(); + } + } + + @Override + public void setWidth(String width) { + super.setWidth(width); + if (BrowserInfo.get().isIE7()) { + /* + * IE7 apparently has problems with calculating width for + * floated elements inside a DIV with padding. Set the width + * explicitly for the caption. + */ + fixTextWidth(); + } + } + + private void fixTextWidth() { + Element captionText = getTextElement(); + int captionWidth = Util.getRequiredWidth(captionText); + int scrollWidth = captionText.getScrollWidth(); + if (scrollWidth > captionWidth) { + captionWidth = scrollWidth; + } + captionText.getStyle().setPropertyPx("width", captionWidth); + } + + } + + class TabBar extends ComplexPanel implements ClickListener { + + private Element tr = DOM.createTR(); + + private Element spacerTd = DOM.createTD(); + + TabBar() { + Element el = DOM.createTable(); + Element tbody = DOM.createTBody(); + DOM.appendChild(el, tbody); + DOM.appendChild(tbody, tr); + setStyleName(spacerTd, CLASSNAME + "-spacertd"); + DOM.appendChild(tr, spacerTd); + DOM.appendChild(spacerTd, DOM.createDiv()); + setElement(el); + } + + protected Element getContainerElement() { + return tr; + } + + private Widget oldSelected; + + public int getTabCount() { + return getWidgetCount(); + } + + public void addTab(VCaption c) { + Element td = DOM.createTD(); + setStyleName(td, CLASSNAME + "-tabitemcell"); + + if (getWidgetCount() == 0) { + setStyleName(td, CLASSNAME + "-tabitemcell-first", true); + } + + Element div = DOM.createDiv(); + setStyleName(div, CLASSNAME + "-tabitem"); + DOM.appendChild(td, div); + DOM.insertBefore(tr, td, spacerTd); + c.addClickListener(this); + add(c, div); + } + + public void onClick(Widget sender) { + int index = getWidgetIndex(sender); + onTabSelected(index); + } + + public void selectTab(int index) { + Widget newSelected = getWidget(index); + Widget.setStyleName(DOM.getParent(newSelected.getElement()), + CLASSNAME + "-tabitem-selected", true); + if (oldSelected != null && oldSelected != newSelected) { + Widget.setStyleName(DOM.getParent(oldSelected.getElement()), + CLASSNAME + "-tabitem-selected", false); + } + oldSelected = newSelected; + } + + public void removeTab(int i) { + Widget w = getWidget(i); + if (w == null) { + return; + } + + Element caption = w.getElement(); + Element div = DOM.getParent(caption); + Element td = DOM.getParent(div); + Element tr = DOM.getParent(td); + remove(w); + + /* + * Widget is the Caption but we want to remove everything up to and + * including the parent TD + */ + + DOM.removeChild(tr, td); + + /* + * If this widget was selected we need to unmark it as the last + * selected + */ + if (w == oldSelected) { + oldSelected = null; + } + } + + @Override + public boolean remove(Widget w) { + ((VCaption) w).removeClickListener(this); + return super.remove(w); + } + + public TabSheetCaption getTab(int index) { + if (index >= getWidgetCount()) { + return null; + } + return (TabSheetCaption) getWidget(index); + } + + public void setVisible(int index, boolean visible) { + Element e = DOM.getParent(getTab(index).getElement()); + if (visible) { + DOM.setStyleAttribute(e, "display", ""); + } else { + DOM.setStyleAttribute(e, "display", "none"); + } + } + + public void updateCaptionSize(int index) { + VCaption c = getTab(index); + c.setWidth(c.getRequiredWidth() + "px"); + + } + + } + + public static final String CLASSNAME = "i-tabsheet"; + + public static final String TABS_CLASSNAME = "i-tabsheet-tabcontainer"; + public static final String SCROLLER_CLASSNAME = "i-tabsheet-scroller"; + private final Element tabs; // tabbar and 'scroller' container + private final Element scroller; // tab-scroller element + private final Element scrollerNext; // tab-scroller next button element + private final Element scrollerPrev; // tab-scroller prev button element + private int scrollerIndex = 0; + + private final TabBar tb = new TabBar(); + private final VTabsheetPanel tp = new VTabsheetPanel(); + private final Element contentNode, deco; + + private final HashMap captions = new HashMap(); + + private String height; + private String width; + + private boolean waitingForResponse; + + private RenderInformation renderInformation = new RenderInformation(); + + /** + * Previous visible widget is set invisible with CSS (not display: none, but + * visibility: hidden), to avoid flickering during render process. Normal + * visibility must be returned later when new widget is rendered. + */ + private Widget previousVisibleWidget; + + private boolean rendering = false; + + private void onTabSelected(final int tabIndex) { + if (disabled || waitingForResponse) { + return; + } + final Object tabKey = tabKeys.get(tabIndex); + if (disabledTabKeys.contains(tabKey)) { + return; + } + if (client != null && activeTabIndex != tabIndex) { + tb.selectTab(tabIndex); + addStyleDependentName("loading"); + // run updating variables in deferred command to bypass some FF + // optimization issues + DeferredCommand.addCommand(new Command() { + public void execute() { + previousVisibleWidget = tp.getWidget(tp.getVisibleWidget()); + DOM.setStyleAttribute(DOM.getParent(previousVisibleWidget + .getElement()), "visibility", "hidden"); + client.updateVariable(id, "selected", tabKeys.get(tabIndex) + .toString(), true); + } + }); + waitingForResponse = true; + } + } + + private boolean isDynamicWidth() { + return width == null || width.equals(""); + } + + private boolean isDynamicHeight() { + return height == null || height.equals(""); + } + + public VTabsheet() { + super(CLASSNAME); + + // Tab scrolling + DOM.setStyleAttribute(getElement(), "overflow", "hidden"); + tabs = DOM.createDiv(); + DOM.setElementProperty(tabs, "className", TABS_CLASSNAME); + scroller = DOM.createDiv(); + + DOM.setElementProperty(scroller, "className", SCROLLER_CLASSNAME); + scrollerPrev = DOM.createButton(); + DOM.setElementProperty(scrollerPrev, "className", SCROLLER_CLASSNAME + + "Prev"); + DOM.sinkEvents(scrollerPrev, Event.ONCLICK); + scrollerNext = DOM.createButton(); + DOM.setElementProperty(scrollerNext, "className", SCROLLER_CLASSNAME + + "Next"); + DOM.sinkEvents(scrollerNext, Event.ONCLICK); + DOM.appendChild(getElement(), tabs); + + // Tabs + tp.setStyleName(CLASSNAME + "-tabsheetpanel"); + contentNode = DOM.createDiv(); + + deco = DOM.createDiv(); + + addStyleDependentName("loading"); // Indicate initial progress + tb.setStyleName(CLASSNAME + "-tabs"); + DOM + .setElementProperty(contentNode, "className", CLASSNAME + + "-content"); + DOM.setElementProperty(deco, "className", CLASSNAME + "-deco"); + + add(tb, tabs); + DOM.appendChild(scroller, scrollerPrev); + DOM.appendChild(scroller, scrollerNext); + + DOM.appendChild(getElement(), contentNode); + add(tp, contentNode); + DOM.appendChild(getElement(), deco); + + DOM.appendChild(tabs, scroller); + + // TODO Use for Safari only. Fix annoying 1px first cell in TabBar. + // DOM.setStyleAttribute(DOM.getFirstChild(DOM.getFirstChild(DOM + // .getFirstChild(tb.getElement()))), "display", "none"); + + } + + @Override + public void onBrowserEvent(Event event) { + + // Tab scrolling + if (isScrolledTabs() && DOM.eventGetTarget(event) == scrollerPrev) { + if (scrollerIndex > 0) { + scrollerIndex--; + DOM.setStyleAttribute(DOM.getChild(DOM.getFirstChild(DOM + .getFirstChild(tb.getElement())), scrollerIndex), + "display", ""); + tb.updateCaptionSize(scrollerIndex); + updateTabScroller(); + } + } else if (isClippedTabs() && DOM.eventGetTarget(event) == scrollerNext) { + int tabs = tb.getTabCount(); + if (scrollerIndex + 1 <= tabs) { + DOM.setStyleAttribute(DOM.getChild(DOM.getFirstChild(DOM + .getFirstChild(tb.getElement())), scrollerIndex), + "display", "none"); + tb.updateCaptionSize(scrollerIndex); + scrollerIndex++; + updateTabScroller(); + } + } else { + super.onBrowserEvent(event); + } + } + + @Override + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + rendering = true; + + super.updateFromUIDL(uidl, client); + if (cachedUpdate) { + return; + } + + // Add proper stylenames for all elements (easier to prevent unwanted + // style inheritance) + if (uidl.hasAttribute("style")) { + final String[] styles = uidl.getStringAttribute("style").split(" "); + final String contentBaseClass = CLASSNAME + "-content"; + String contentClass = contentBaseClass; + final String decoBaseClass = CLASSNAME + "-deco"; + String decoClass = decoBaseClass; + for (int i = 0; i < styles.length; i++) { + tb.addStyleDependentName(styles[i]); + contentClass += " " + contentBaseClass + "-" + styles[i]; + decoClass += " " + decoBaseClass + "-" + styles[i]; + } + DOM.setElementProperty(contentNode, "className", contentClass); + DOM.setElementProperty(deco, "className", decoClass); + } else { + tb.setStyleName(CLASSNAME + "-tabs"); + DOM.setElementProperty(contentNode, "className", CLASSNAME + + "-content"); + DOM.setElementProperty(deco, "className", CLASSNAME + "-deco"); + } + + if (uidl.hasAttribute("hidetabs")) { + tb.setVisible(false); + addStyleName(CLASSNAME + "-hidetabs"); + } else { + tb.setVisible(true); + removeStyleName(CLASSNAME + "-hidetabs"); + } + + // tabs; push or not + if (!isDynamicWidth()) { + // FIXME: This makes tab sheet tabs go to 1px width on every update + // and then back to original width + // update width later, in updateTabScroller(); + DOM.setStyleAttribute(tabs, "width", "1px"); + DOM.setStyleAttribute(tabs, "overflow", "hidden"); + } else { + showAllTabs(); + DOM.setStyleAttribute(tabs, "width", ""); + DOM.setStyleAttribute(tabs, "overflow", "visible"); + updateDynamicWidth(); + } + + if (!isDynamicHeight()) { + // Must update height after the styles have been set + updateContentNodeHeight(); + updateOpenTabSize(); + } + + iLayout(); + + // Re run relative size update to ensure optimal scrollbars + // TODO isolate to situation that visible tab has undefined height + try { + client.handleComponentRelativeSize(tp.getWidget(tp + .getVisibleWidget())); + } catch (Exception e) { + // Ignore, most likely empty tabsheet + } + + renderInformation.updateSize(getElement()); + + waitingForResponse = false; + rendering = false; + } + + private void updateDynamicWidth() { + // Find tab width + int tabsWidth = 0; + + int count = tb.getTabCount(); + for (int i = 0; i < count; i++) { + Element tabTd = tb.getTab(i).getElement().getParentElement().cast(); + tabsWidth += tabTd.getOffsetWidth(); + } + + // Find content width + Style style = tp.getElement().getStyle(); + String overflow = style.getProperty("overflow"); + style.setProperty("overflow", "hidden"); + style.setPropertyPx("width", tabsWidth); + Style wrapperstyle = tp.getWidget(tp.getVisibleWidget()).getElement() + .getParentElement().getStyle(); + wrapperstyle.setPropertyPx("width", tabsWidth); + // Get content width from actual widget + + int contentWidth = 0; + if (tp.getWidgetCount() > 0) { + contentWidth = tp.getWidget(tp.getVisibleWidget()).getOffsetWidth(); + } + style.setProperty("overflow", overflow); + + // Set widths to max(tabs,content) + if (tabsWidth < contentWidth) { + tabsWidth = contentWidth; + } + + int outerWidth = tabsWidth + getContentAreaBorderWidth(); + + tabs.getStyle().setPropertyPx("width", outerWidth); + style.setPropertyPx("width", tabsWidth); + wrapperstyle.setPropertyPx("width", tabsWidth); + + contentNode.getStyle().setPropertyPx("width", tabsWidth); + super.setWidth(outerWidth + "px"); + updateOpenTabSize(); + } + + @Override + protected void renderTab(final UIDL tabUidl, int index, boolean selected, + boolean hidden) { + TabSheetCaption c = tb.getTab(index); + if (c == null) { + c = new TabSheetCaption(); + tb.addTab(c); + } + c.updateCaption(tabUidl); + + tb.setVisible(index, !hidden); + + /* + * Force the width of the caption container so the content will not wrap + * and tabs won't be too narrow in certain browsers + */ + c.setWidth(c.getRequiredWidth() + "px"); + captions.put("" + index, c); + + UIDL tabContentUIDL = null; + Paintable tabContent = null; + if (tabUidl.getChildCount() > 0) { + tabContentUIDL = tabUidl.getChildUIDL(0); + tabContent = client.getPaintable(tabContentUIDL); + } + + if (tabContent != null) { + /* This is a tab with content information */ + + int oldIndex = tp.getWidgetIndex((Widget) tabContent); + if (oldIndex != -1 && oldIndex != index) { + /* + * The tab has previously been rendered in another position so + * we must move the cached content to correct position + */ + tp.insert((Widget) tabContent, index); + } + } else { + /* A tab whose content has not yet been loaded */ + + /* + * Make sure there is a corresponding empty tab in tp. The same + * operation as the moving above but for not-loaded tabs. + */ + if (index < tp.getWidgetCount()) { + Widget oldWidget = tp.getWidget(index); + if (!(oldWidget instanceof PlaceHolder)) { + tp.insert(new PlaceHolder(), index); + } + } + + } + + if (selected) { + renderContent(tabContentUIDL); + tb.selectTab(index); + } else { + if (tabContentUIDL != null) { + // updating a drawn child on hidden tab + if (tp.getWidgetIndex((Widget) tabContent) < 0) { + tp.insert((Widget) tabContent, index); + } + tabContent.updateFromUIDL(tabContentUIDL, client); + } else if (tp.getWidgetCount() <= index) { + tp.add(new PlaceHolder()); + } + } + } + + public class PlaceHolder extends VLabel { + public PlaceHolder() { + super(""); + } + } + + @Override + protected void selectTab(int index, final UIDL contentUidl) { + if (index != activeTabIndex) { + activeTabIndex = index; + tb.selectTab(activeTabIndex); + } + renderContent(contentUidl); + } + + private void renderContent(final UIDL contentUIDL) { + final Paintable content = client.getPaintable(contentUIDL); + if (tp.getWidgetCount() > activeTabIndex) { + Widget old = tp.getWidget(activeTabIndex); + if (old != content) { + tp.remove(activeTabIndex); + if (old instanceof Paintable) { + client.unregisterPaintable((Paintable) old); + } + tp.insert((Widget) content, activeTabIndex); + } + } else { + tp.add((Widget) content); + } + + tp.showWidget(activeTabIndex); + + VTabsheet.this.iLayout(); + (content).updateFromUIDL(contentUIDL, client); + /* + * The size of a cached, relative sized component must be updated to + * report correct size to updateOpenTabSize(). + */ + if (contentUIDL.getBooleanAttribute("cached")) { + client.handleComponentRelativeSize((Widget) content); + } + updateOpenTabSize(); + VTabsheet.this.removeStyleDependentName("loading"); + if (previousVisibleWidget != null) { + DOM.setStyleAttribute(previousVisibleWidget.getElement(), + "visibility", ""); + previousVisibleWidget = null; + } + } + + @Override + public void setHeight(String height) { + super.setHeight(height); + this.height = height; + updateContentNodeHeight(); + + if (!rendering) { + updateOpenTabSize(); + iLayout(); + // TODO Check if this is needed + client.runDescendentsLayout(this); + } + } + + private void updateContentNodeHeight() { + if (height != null && !"".equals(height)) { + int contentHeight = getOffsetHeight(); + contentHeight -= DOM.getElementPropertyInt(deco, "offsetHeight"); + contentHeight -= tb.getOffsetHeight(); + if (contentHeight < 0) { + contentHeight = 0; + } + + // Set proper values for content element + DOM.setStyleAttribute(contentNode, "height", contentHeight + "px"); + renderSpace.setHeight(contentHeight); + } else { + DOM.setStyleAttribute(contentNode, "height", ""); + renderSpace.setHeight(0); + } + } + + @Override + public void setWidth(String width) { + if ((this.width == null && width.equals("")) + || (this.width != null && this.width.equals(width))) { + return; + } + + super.setWidth(width); + if (width.equals("")) { + width = null; + } + this.width = width; + if (width == null) { + renderSpace.setWidth(0); + contentNode.getStyle().setProperty("width", ""); + } else { + int contentWidth = getOffsetWidth() - getContentAreaBorderWidth(); + if (contentWidth < 0) { + contentWidth = 0; + } + contentNode.getStyle().setProperty("width", contentWidth + "px"); + renderSpace.setWidth(contentWidth); + } + + if (!rendering) { + if (isDynamicHeight()) { + Util.updateRelativeChildrenAndSendSizeUpdateEvent(client, tp, + this); + } + + updateOpenTabSize(); + iLayout(); + // TODO Check if this is needed + client.runDescendentsLayout(this); + + } + + } + + public void iLayout() { + updateTabScroller(); + tp.runWebkitOverflowAutoFix(); + } + + /** + * Sets the size of the visible tab (component). As the tab is set to + * position: absolute (to work around a firefox flickering bug) we must keep + * this up-to-date by hand. + */ + private void updateOpenTabSize() { + /* + * The overflow=auto element must have a height specified, otherwise it + * will be just as high as the contents and no scrollbars will appear + */ + int height = -1; + int width = -1; + int minWidth = 0; + + if (!isDynamicHeight()) { + height = renderSpace.getHeight(); + } + if (!isDynamicWidth()) { + width = renderSpace.getWidth(); + } else { + /* + * If the tabbar is wider than the content we need to use the tabbar + * width as minimum width so scrollbars get placed correctly (at the + * right edge). + */ + minWidth = tb.getOffsetWidth() - getContentAreaBorderWidth(); + } + tp.fixVisibleTabSize(width, height, minWidth); + + } + + /** + * Layouts the tab-scroller elements, and applies styles. + */ + private void updateTabScroller() { + if (width != null) { + DOM.setStyleAttribute(tabs, "width", width); + } + if (scrollerIndex > tb.getTabCount()) { + scrollerIndex = 0; + } + boolean scrolled = isScrolledTabs(); + boolean clipped = isClippedTabs(); + if (tb.isVisible() && (scrolled || clipped)) { + DOM.setStyleAttribute(scroller, "display", ""); + DOM.setElementProperty(scrollerPrev, "className", + SCROLLER_CLASSNAME + (scrolled ? "Prev" : "Prev-disabled")); + DOM.setElementProperty(scrollerNext, "className", + SCROLLER_CLASSNAME + (clipped ? "Next" : "Next-disabled")); + } else { + DOM.setStyleAttribute(scroller, "display", "none"); + } + + if (BrowserInfo.get().isSafari()) { + // fix tab height for safari, bugs sometimes if tabs contain icons + String property = tabs.getStyle().getProperty("height"); + if (property == null || property.equals("")) { + tabs.getStyle().setPropertyPx("height", tb.getOffsetHeight()); + } + /* + * another hack for webkits. tabscroller sometimes drops without + * "shaking it" reproducable in + * com.vaadin.tests.components.tabsheet.TabSheetIcons + */ + final Style style = scroller.getStyle(); + style.setProperty("whiteSpace", "normal"); + DeferredCommand.addCommand(new Command() { + public void execute() { + style.setProperty("whiteSpace", ""); + } + }); + } + + } + + private void showAllTabs() { + scrollerIndex = 0; + Element tr = DOM.getFirstChild(DOM.getFirstChild(tb.getElement())); + for (int i = 0; i < tb.getTabCount(); i++) { + DOM.setStyleAttribute(DOM.getChild(tr, i), "display", ""); + } + } + + private boolean isScrolledTabs() { + return scrollerIndex > 0; + } + + private boolean isClippedTabs() { + return tb.getOffsetWidth() > getOffsetWidth(); + } + + @Override + protected void clearPaintables() { + + int i = tb.getTabCount(); + while (i > 0) { + tb.removeTab(--i); + } + tp.clear(); + + } + + @Override + protected Iterator getPaintableIterator() { + return tp.iterator(); + } + + public boolean hasChildComponent(Widget component) { + if (tp.getWidgetIndex(component) < 0) { + return false; + } else { + return true; + } + } + + public void replaceChildComponent(Widget oldComponent, Widget newComponent) { + tp.replaceComponent(oldComponent, newComponent); + } + + public void updateCaption(Paintable component, UIDL uidl) { + /* Tabsheet does not render its children's captions */ + } + + public boolean requestLayout(Set child) { + if (!isDynamicHeight() && !isDynamicWidth()) { + /* + * If the height and width has been specified for this container the + * child components cannot make the size of the layout change + */ + + return true; + } + + updateOpenTabSize(); + + if (renderInformation.updateSize(getElement())) { + /* + * Size has changed so we let the child components know about the + * new size. + */ + iLayout(); + client.runDescendentsLayout(this); + + return false; + } else { + /* + * Size has not changed so we do not need to propagate the event + * further + */ + return true; + } + + } + + private int borderW = -1; + + private int getContentAreaBorderWidth() { + if (borderW < 0) { + borderW = Util.measureHorizontalBorder(contentNode); + } + return borderW; + } + + private RenderSpace renderSpace = new RenderSpace(0, 0, true); + + public RenderSpace getAllocatedSpace(Widget child) { + // All tabs have equal amount of space allocated + return renderSpace; + } + + @Override + protected int getTabCount() { + return tb.getWidgetCount(); + } + + @Override + protected Paintable getTab(int index) { + if (tp.getWidgetCount() > index) { + return (Paintable) tp.getWidget(index); + } + return null; + } + + @Override + protected void removeTab(int index) { + tb.removeTab(index); + /* + * This must be checked because renderTab automatically removes the + * active tab content when it changes + */ + if (tp.getWidgetCount() > index) { + tp.remove(index); + } + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTabsheetBase.java b/src/com/vaadin/terminal/gwt/client/ui/VTabsheetBase.java new file mode 100644 index 0000000000..6bf77bdde2 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VTabsheetBase.java @@ -0,0 +1,144 @@ +package com.vaadin.terminal.gwt.client.ui; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.ui.ComplexPanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.Container; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; + +abstract class VTabsheetBase extends ComplexPanel implements Container { + + String id; + ApplicationConnection client; + + protected final ArrayList tabKeys = new ArrayList(); + protected int activeTabIndex = 0; + protected boolean disabled; + protected boolean readonly; + protected Set disabledTabKeys = new HashSet(); + protected boolean cachedUpdate = false; + + public VTabsheetBase(String classname) { + setElement(DOM.createDiv()); + setStylePrimaryName(classname); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + this.client = client; + + // Ensure correct implementation + cachedUpdate = client.updateComponent(this, uidl, true); + if (cachedUpdate) { + return; + } + + // Update member references + id = uidl.getId(); + disabled = uidl.hasAttribute("disabled"); + + // Render content + final UIDL tabs = uidl.getChildUIDL(0); + + // Paintables in the TabSheet before update + ArrayList oldPaintables = new ArrayList(); + for (Iterator iterator = getPaintableIterator(); iterator.hasNext();) { + oldPaintables.add(iterator.next()); + } + + // Clear previous values + tabKeys.clear(); + disabledTabKeys.clear(); + + int index = 0; + for (final Iterator it = tabs.getChildIterator(); it.hasNext();) { + final UIDL tab = (UIDL) it.next(); + final String key = tab.getStringAttribute("key"); + final boolean selected = tab.getBooleanAttribute("selected"); + final boolean hidden = tab.getBooleanAttribute("hidden"); + + if (tab.getBooleanAttribute("disabled")) { + disabledTabKeys.add(key); + } + + tabKeys.add(key); + + if (selected) { + activeTabIndex = index; + } + renderTab(tab, index, selected, hidden); + index++; + } + + int tabCount = getTabCount(); + while (tabCount-- > index) { + removeTab(index); + } + + for (int i = 0; i < getTabCount(); i++) { + Paintable p = getTab(i); + oldPaintables.remove(p); + } + + // Perform unregister for any paintables removed during update + for (Iterator iterator = oldPaintables.iterator(); iterator.hasNext();) { + Object oldPaintable = iterator.next(); + if (oldPaintable instanceof Paintable) { + Widget w = (Widget) oldPaintable; + if (w.isAttached()) { + w.removeFromParent(); + } + client.unregisterPaintable((Paintable) oldPaintable); + } + } + + } + + /** + * @return a list of currently shown Paintables + */ + abstract protected Iterator getPaintableIterator(); + + /** + * Clears current tabs and contents + */ + abstract protected void clearPaintables(); + + /** + * Implement in extending classes. This method should render needed elements + * and set the visibility of the tab according to the 'selected' parameter. + */ + protected abstract void renderTab(final UIDL tabUidl, int index, + boolean selected, boolean hidden); + + /** + * Implement in extending classes. This method should render any previously + * non-cached content and set the activeTabIndex property to the specified + * index. + */ + protected abstract void selectTab(int index, final UIDL contentUidl); + + /** + * Implement in extending classes. This method should return the number of + * tabs currently rendered. + */ + protected abstract int getTabCount(); + + /** + * Implement in extending classes. This method should return the Paintable + * corresponding to the given index. + */ + protected abstract Paintable getTab(int index); + + /** + * Implement in extending classes. This method should remove the rendered + * tab with the specified index. + */ + protected abstract void removeTab(int index); +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTabsheetPanel.java b/src/com/vaadin/terminal/gwt/client/ui/VTabsheetPanel.java new file mode 100644 index 0000000000..cc8168337e --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VTabsheetPanel.java @@ -0,0 +1,183 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.ComplexPanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.Util; + +/** + * A panel that displays all of its child widgets in a 'deck', where only one + * can be visible at a time. It is used by + * {@link com.vaadin.terminal.gwt.client.ui.VTabsheet}. + * + * This class has the same basic functionality as the GWT DeckPanel + * {@link com.google.gwt.user.client.ui.DeckPanel}, with the exception that it + * doesn't manipulate the child widgets' width and height attributes. + */ +public class VTabsheetPanel extends ComplexPanel { + + private Widget visibleWidget; + + /** + * Creates an empty tabsheet panel. + */ + public VTabsheetPanel() { + setElement(DOM.createDiv()); + } + + /** + * Adds the specified widget to the deck. + * + * @param w + * the widget to be added + */ + @Override + public void add(Widget w) { + Element el = createContainerElement(); + DOM.appendChild(getElement(), el); + super.add(w, el); + } + + private Element createContainerElement() { + Element el = DOM.createDiv(); + DOM.setStyleAttribute(el, "position", "absolute"); + DOM.setStyleAttribute(el, "overflow", "auto"); + hide(el); + return el; + } + + /** + * Gets the index of the currently-visible widget. + * + * @return the visible widget's index + */ + public int getVisibleWidget() { + return getWidgetIndex(visibleWidget); + } + + /** + * Inserts a widget before the specified index. + * + * @param w + * the widget to be inserted + * @param beforeIndex + * the index before which it will be inserted + * @throws IndexOutOfBoundsException + * if beforeIndex is out of range + */ + public void insert(Widget w, int beforeIndex) { + Element el = createContainerElement(); + DOM.insertChild(getElement(), el, beforeIndex); + super.insert(w, el, beforeIndex, false); + } + + @Override + public boolean remove(Widget w) { + Element child = w.getElement(); + Element parent = null; + if (child != null) { + parent = DOM.getParent(child); + } + final boolean removed = super.remove(w); + if (removed) { + if (visibleWidget == w) { + visibleWidget = null; + } + if (parent != null) { + DOM.removeChild(getElement(), parent); + } + } + return removed; + } + + /** + * Shows the widget at the specified index. This causes the currently- + * visible widget to be hidden. + * + * @param index + * the index of the widget to be shown + */ + public void showWidget(int index) { + checkIndexBoundsForAccess(index); + Widget newVisible = getWidget(index); + if (visibleWidget != newVisible) { + if (visibleWidget != null) { + hide(DOM.getParent(visibleWidget.getElement())); + } + visibleWidget = newVisible; + unHide(DOM.getParent(visibleWidget.getElement())); + } + } + + private void hide(Element e) { + DOM.setStyleAttribute(e, "visibility", "hidden"); + DOM.setStyleAttribute(e, "top", "-100000px"); + DOM.setStyleAttribute(e, "left", "-100000px"); + } + + private void unHide(Element e) { + DOM.setStyleAttribute(e, "top", "0px"); + DOM.setStyleAttribute(e, "left", "0px"); + DOM.setStyleAttribute(e, "visibility", ""); + } + + public void fixVisibleTabSize(int width, int height, int minWidth) { + if (visibleWidget == null) { + return; + } + + boolean dynamicHeight = false; + + if (height < 0) { + height = visibleWidget.getOffsetHeight(); + dynamicHeight = true; + } + if (width < 0) { + width = visibleWidget.getOffsetWidth(); + } + if (width < minWidth) { + width = minWidth; + } + + Element wrapperDiv = (Element) visibleWidget.getElement() + .getParentElement(); + + // width first + getElement().getStyle().setPropertyPx("width", width); + wrapperDiv.getStyle().setPropertyPx("width", width); + + if (dynamicHeight) { + // height of widget might have changed due wrapping + height = visibleWidget.getOffsetHeight(); + } + // i-tabsheet-tabsheetpanel height + getElement().getStyle().setPropertyPx("height", height); + + // widget wrapper height + wrapperDiv.getStyle().setPropertyPx("height", height); + runWebkitOverflowAutoFix(); + } + + public void runWebkitOverflowAutoFix() { + if (visibleWidget != null) { + Util.runWebkitOverflowAutoFix(DOM.getParent(visibleWidget + .getElement())); + } + + } + + public void replaceComponent(Widget oldComponent, Widget newComponent) { + boolean isVisible = (visibleWidget == oldComponent); + int widgetIndex = getWidgetIndex(oldComponent); + remove(oldComponent); + insert(newComponent, widgetIndex); + if (isVisible) { + showWidget(widgetIndex); + } + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTextArea.java b/src/com/vaadin/terminal/gwt/client/ui/VTextArea.java new file mode 100644 index 0000000000..29741afc57 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VTextArea.java @@ -0,0 +1,72 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.DeferredCommand; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.UIDL; + +/** + * This class represents a multiline textfield (textarea). + * + * TODO consider replacing this with a RichTextArea based implementation. IE + * does not support CSS height for textareas in Strict mode :-( + * + * @author IT Mill Ltd. + * + */ +public class VTextArea extends VTextField { + public static final String CLASSNAME = "i-textarea"; + + public VTextArea() { + super(DOM.createTextArea()); + setStyleName(CLASSNAME); + } + + @Override + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + // Call parent renderer explicitly + super.updateFromUIDL(uidl, client); + + if (uidl.hasAttribute("rows")) { + setRows(new Integer(uidl.getStringAttribute("rows")).intValue()); + } + + if (getMaxLength() >= 0) { + sinkEvents(Event.ONKEYPRESS); + } + } + + public void setRows(int rows) { + setRows(getElement(), rows); + } + + private native void setRows(Element e, int r) + /*-{ + try { + if(e.tagName.toLowerCase() == "textarea") + e.rows = r; + } catch (e) {} + }-*/; + + @Override + public void onBrowserEvent(Event event) { + if (getMaxLength() >= 0 && event.getTypeInt() == Event.ONKEYPRESS) { + DeferredCommand.addCommand(new Command() { + public void execute() { + if (getText().length() > getMaxLength()) { + setText(getText().substring(0, getMaxLength())); + } + } + }); + } + super.onBrowserEvent(event); + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTextField.java b/src/com/vaadin/terminal/gwt/client/ui/VTextField.java new file mode 100644 index 0000000000..7c14a4dd2c --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VTextField.java @@ -0,0 +1,270 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.ChangeListener; +import com.google.gwt.user.client.ui.FocusListener; +import com.google.gwt.user.client.ui.TextBoxBase; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.VTooltip; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +/** + * This class represents a basic text input field with one row. + * + * @author IT Mill Ltd. + * + */ +public class VTextField extends TextBoxBase implements Paintable, Field, + ChangeListener, FocusListener { + + /** + * The input node CSS classname. + */ + public static final String CLASSNAME = "i-textfield"; + /** + * This CSS classname is added to the input node on hover. + */ + public static final String CLASSNAME_FOCUS = "focus"; + + protected String id; + + protected ApplicationConnection client; + + private String valueBeforeEdit = null; + + private boolean immediate = false; + private int extraHorizontalPixels = -1; + private int extraVerticalPixels = -1; + private int maxLength = -1; + + private static final String CLASSNAME_PROMPT = "prompt"; + private static final String ATTR_INPUTPROMPT = "prompt"; + private String inputPrompt = null; + private boolean prompting = false; + + public VTextField() { + this(DOM.createInputText()); + } + + protected VTextField(Element node) { + super(node); + if (BrowserInfo.get().isIE()) { + // Fixes IE margin problem (#2058) + DOM.setStyleAttribute(node, "marginTop", "-1px"); + DOM.setStyleAttribute(node, "marginBottom", "-1px"); + } + setStyleName(CLASSNAME); + addChangeListener(this); + addFocusListener(this); + sinkEvents(VTooltip.TOOLTIP_EVENTS); + } + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + if (client != null) { + client.handleTooltipEvent(event, this); + } + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + this.client = client; + id = uidl.getId(); + + if (client.updateComponent(this, uidl, true)) { + return; + } + + if (uidl.getBooleanAttribute("readonly")) { + setReadOnly(true); + } else { + setReadOnly(false); + } + + inputPrompt = uidl.getStringAttribute(ATTR_INPUTPROMPT); + + setMaxLength(uidl.hasAttribute("maxLength") ? uidl + .getIntAttribute("maxLength") : -1); + + immediate = uidl.getBooleanAttribute("immediate"); + + if (uidl.hasAttribute("cols")) { + setColumns(new Integer(uidl.getStringAttribute("cols")).intValue()); + } + + String text = uidl.getStringVariable("text"); + prompting = inputPrompt != null && (text == null || text.equals("")); + if (prompting) { + setText(inputPrompt); + addStyleDependentName(CLASSNAME_PROMPT); + } else { + setText(text); + removeStyleDependentName(CLASSNAME_PROMPT); + } + valueBeforeEdit = uidl.getStringVariable("text"); + } + + private void setMaxLength(int newMaxLength) { + if (newMaxLength > 0) { + maxLength = newMaxLength; + if (getElement().getTagName().toLowerCase().equals("textarea")) { + // NOP no maxlength property for textarea + } else { + getElement().setPropertyInt("maxLength", maxLength); + } + } else if (maxLength != -1) { + if (getElement().getTagName().toLowerCase().equals("textarea")) { + // NOP no maxlength property for textarea + } else { + getElement().setAttribute("maxlength", ""); + } + maxLength = -1; + } + + } + + protected int getMaxLength() { + return maxLength; + } + + public void onChange(Widget sender) { + if (client != null && id != null) { + String newText = getText(); + if (!prompting && newText != null + && !newText.equals(valueBeforeEdit)) { + client.updateVariable(id, "text", getText(), immediate); + valueBeforeEdit = newText; + } + } + } + + private static VTextField focusedTextField; + + public static void flushChangesFromFocusedTextField() { + if (focusedTextField != null) { + focusedTextField.onChange(null); + } + } + + public void onFocus(Widget sender) { + addStyleDependentName(CLASSNAME_FOCUS); + if (prompting) { + setText(""); + removeStyleDependentName(CLASSNAME_PROMPT); + } + focusedTextField = this; + } + + public void onLostFocus(Widget sender) { + removeStyleDependentName(CLASSNAME_FOCUS); + focusedTextField = null; + String text = getText(); + prompting = inputPrompt != null && (text == null || "".equals(text)); + if (prompting) { + setText(inputPrompt); + addStyleDependentName(CLASSNAME_PROMPT); + } + onChange(sender); + } + + public void setColumns(int columns) { + setColumns(getElement(), columns); + } + + private native void setColumns(Element e, int c) + /*-{ + try { + switch(e.tagName.toLowerCase()) { + case "input": + //e.size = c; + e.style.width = c+"em"; + break; + case "textarea": + //e.cols = c; + e.style.width = c+"em"; + break; + default:; + } + } catch (e) {} + }-*/; + + /** + * @return space used by components paddings and borders + */ + private int getExtraHorizontalPixels() { + if (extraHorizontalPixels < 0) { + detectExtraSizes(); + } + return extraHorizontalPixels; + } + + /** + * @return space used by components paddings and borders + */ + private int getExtraVerticalPixels() { + if (extraVerticalPixels < 0) { + detectExtraSizes(); + } + return extraVerticalPixels; + } + + /** + * Detects space used by components paddings and borders. Used when + * relational size are used. + */ + private void detectExtraSizes() { + Element clone = Util.cloneNode(getElement(), false); + DOM.setElementAttribute(clone, "id", ""); + DOM.setStyleAttribute(clone, "visibility", "hidden"); + DOM.setStyleAttribute(clone, "position", "absolute"); + // due FF3 bug set size to 10px and later subtract it from extra pixels + DOM.setStyleAttribute(clone, "width", "10px"); + DOM.setStyleAttribute(clone, "height", "10px"); + DOM.appendChild(DOM.getParent(getElement()), clone); + extraHorizontalPixels = DOM.getElementPropertyInt(clone, "offsetWidth") - 10; + extraVerticalPixels = DOM.getElementPropertyInt(clone, "offsetHeight") - 10; + + DOM.removeChild(DOM.getParent(getElement()), clone); + } + + @Override + public void setHeight(String height) { + if (height.endsWith("px")) { + int h = Integer.parseInt(height.substring(0, height.length() - 2)); + h -= getExtraVerticalPixels(); + if (h < 0) { + h = 0; + } + + super.setHeight(h + "px"); + } else { + super.setHeight(height); + } + } + + @Override + public void setWidth(String width) { + if (width.endsWith("px")) { + int w = Integer.parseInt(width.substring(0, width.length() - 2)); + w -= getExtraHorizontalPixels(); + if (w < 0) { + w = 0; + } + + super.setWidth(w + "px"); + } else { + super.setWidth(width); + } + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTextualDate.java b/src/com/vaadin/terminal/gwt/client/ui/VTextualDate.java new file mode 100644 index 0000000000..37a70ed7f0 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VTextualDate.java @@ -0,0 +1,301 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.Date; + +import com.google.gwt.i18n.client.DateTimeFormat; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.ui.ChangeListener; +import com.google.gwt.user.client.ui.TextBox; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.ClientExceptionHandler; +import com.vaadin.terminal.gwt.client.ContainerResizedListener; +import com.vaadin.terminal.gwt.client.Focusable; +import com.vaadin.terminal.gwt.client.LocaleNotLoadedException; +import com.vaadin.terminal.gwt.client.LocaleService; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; + +public class VTextualDate extends VDateField implements Paintable, Field, + ChangeListener, ContainerResizedListener, Focusable { + + private static final String PARSE_ERROR_CLASSNAME = CLASSNAME + + "-parseerror"; + + private final TextBox text; + + private String formatStr; + + private String width; + + private boolean needLayout; + + protected int fieldExtraWidth = -1; + + public VTextualDate() { + super(); + text = new TextBox(); + // use normal textfield styles as a basis + text.setStyleName(VTextField.CLASSNAME); + // add datefield spesific style name also + text.addStyleName(CLASSNAME + "-textfield"); + text.addChangeListener(this); + add(text); + } + + @Override + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + + int origRes = currentResolution; + super.updateFromUIDL(uidl, client); + if (origRes != currentResolution) { + // force recreating format string + formatStr = null; + } + if (uidl.hasAttribute("format")) { + formatStr = uidl.getStringAttribute("format"); + } + + buildDate(); + // not a FocusWidget -> needs own tabindex handling + if (uidl.hasAttribute("tabindex")) { + text.setTabIndex(uidl.getIntAttribute("tabindex")); + } + } + + protected String getFormatString() { + if (formatStr == null) { + if (currentResolution == RESOLUTION_YEAR) { + formatStr = "yyyy"; // force full year + } else { + + try { + String frmString = LocaleService + .getDateFormat(currentLocale); + frmString = cleanFormat(frmString); + String delim = LocaleService + .getClockDelimiter(currentLocale); + + if (currentResolution >= RESOLUTION_HOUR) { + if (dts.isTwelveHourClock()) { + frmString += " hh"; + } else { + frmString += " HH"; + } + if (currentResolution >= RESOLUTION_MIN) { + frmString += ":mm"; + if (currentResolution >= RESOLUTION_SEC) { + frmString += ":ss"; + if (currentResolution >= RESOLUTION_MSEC) { + frmString += ".SSS"; + } + } + } + if (dts.isTwelveHourClock()) { + frmString += " aaa"; + } + + } + + formatStr = frmString; + } catch (LocaleNotLoadedException e) { + ClientExceptionHandler.displayError(e); + } + } + } + return formatStr; + } + + /** + * + */ + protected void buildDate() { + removeStyleName(PARSE_ERROR_CLASSNAME); + // Create the initial text for the textfield + String dateText; + if (date != null) { + dateText = DateTimeFormat.getFormat(getFormatString()).format(date); + } else { + dateText = ""; + } + + text.setText(dateText); + text.setEnabled(enabled && !readonly); + + if (readonly) { + text.addStyleName("i-readonly"); + } else { + text.removeStyleName("i-readonly"); + } + + } + + public void onChange(Widget sender) { + if (sender == text) { + if (!text.getText().equals("")) { + try { + DateTimeFormat format = DateTimeFormat + .getFormat(getFormatString()); + date = format.parse(text.getText()); + long stamp = date.getTime(); + if (stamp == 0) { + // If date parsing fails in firefox the stamp will be 0 + date = null; + addStyleName(PARSE_ERROR_CLASSNAME); + } else { + // remove possibly added invalid value indication + removeStyleName(PARSE_ERROR_CLASSNAME); + } + } catch (final Exception e) { + ClientExceptionHandler.displayError(e.getMessage()); + + addStyleName(PARSE_ERROR_CLASSNAME); + // this is a hack that may eventually be removed + client.updateVariable(id, "lastInvalidDateString", text + .getText(), false); + date = null; + } + } else { + date = null; + // remove possibly added invalid value indication + removeStyleName(PARSE_ERROR_CLASSNAME); + } + // always send the date string + client.updateVariable(id, "dateString", text.getText(), false); + + if (date != null) { + showingDate = new Date(date.getTime()); + } + + // Update variables + // (only the smallest defining resolution needs to be + // immediate) + client.updateVariable(id, "year", + date != null ? date.getYear() + 1900 : -1, + currentResolution == VDateField.RESOLUTION_YEAR + && immediate); + if (currentResolution >= VDateField.RESOLUTION_MONTH) { + client.updateVariable(id, "month", date != null ? date + .getMonth() + 1 : -1, + currentResolution == VDateField.RESOLUTION_MONTH + && immediate); + } + if (currentResolution >= VDateField.RESOLUTION_DAY) { + client.updateVariable(id, "day", date != null ? date.getDate() + : -1, currentResolution == VDateField.RESOLUTION_DAY + && immediate); + } + if (currentResolution >= VDateField.RESOLUTION_HOUR) { + client.updateVariable(id, "hour", date != null ? date + .getHours() : -1, + currentResolution == VDateField.RESOLUTION_HOUR + && immediate); + } + if (currentResolution >= VDateField.RESOLUTION_MIN) { + client.updateVariable(id, "min", date != null ? date + .getMinutes() : -1, + currentResolution == VDateField.RESOLUTION_MIN + && immediate); + } + if (currentResolution >= VDateField.RESOLUTION_SEC) { + client.updateVariable(id, "sec", date != null ? date + .getSeconds() : -1, + currentResolution == VDateField.RESOLUTION_SEC + && immediate); + } + if (currentResolution == VDateField.RESOLUTION_MSEC) { + client.updateVariable(id, "msec", + date != null ? getMilliseconds() : -1, immediate); + } + + } + } + + private String cleanFormat(String format) { + // Remove unnecessary d & M if resolution is too low + if (currentResolution < VDateField.RESOLUTION_DAY) { + format = format.replaceAll("d", ""); + } + if (currentResolution < VDateField.RESOLUTION_MONTH) { + format = format.replaceAll("M", ""); + } + + // Remove unsupported patterns + // TODO support for 'G', era designator (used at least in Japan) + format = format.replaceAll("[GzZwWkK]", ""); + + // Remove extra delimiters ('/' and '.') + while (format.startsWith("/") || format.startsWith(".") + || format.startsWith("-")) { + format = format.substring(1); + } + while (format.endsWith("/") || format.endsWith(".") + || format.endsWith("-")) { + format = format.substring(0, format.length() - 1); + } + + // Remove duplicate delimiters + format = format.replaceAll("//", "/"); + format = format.replaceAll("\\.\\.", "."); + format = format.replaceAll("--", "-"); + + return format.trim(); + } + + @Override + public void setWidth(String newWidth) { + if (!"".equals(newWidth) && (width == null || !newWidth.equals(width))) { + if (BrowserInfo.get().isIE6()) { + // in IE6 cols ~ min-width + DOM.setElementProperty(text.getElement(), "size", "1"); + } + needLayout = true; + width = newWidth; + super.setWidth(width); + iLayout(); + if (newWidth.indexOf("%") < 0) { + needLayout = false; + } + } else { + if ("".equals(newWidth) && width != null && !"".equals(width)) { + if (BrowserInfo.get().isIE6()) { + // revert IE6 hack + DOM.setElementProperty(text.getElement(), "size", ""); + } + super.setWidth(""); + needLayout = true; + iLayout(); + needLayout = false; + width = null; + } + } + } + + /** + * Returns pixels in x-axis reserved for other than textfield content. + * + * @return extra width in pixels + */ + protected int getFieldExtraWidth() { + if (fieldExtraWidth < 0) { + text.setWidth("0px"); + fieldExtraWidth = text.getOffsetWidth(); + } + return fieldExtraWidth; + } + + public void iLayout() { + if (needLayout) { + text.setWidth((getOffsetWidth() - getFieldExtraWidth()) + "px"); + } + } + + public void focus() { + text.setFocus(true); + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTime.java b/src/com/vaadin/terminal/gwt/client/ui/VTime.java new file mode 100644 index 0000000000..8b9c1e0199 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VTime.java @@ -0,0 +1,317 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.Date; + +import com.google.gwt.user.client.ui.ChangeListener; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.ListBox; +import com.google.gwt.user.client.ui.Widget; + +public class VTime extends FlowPanel implements ChangeListener { + + private final VDateField datefield; + + private ListBox hours; + + private ListBox mins; + + private ListBox sec; + + private ListBox msec; + + private ListBox ampm; + + private int resolution = VDateField.RESOLUTION_HOUR; + + private boolean readonly; + + public VTime(VDateField parent) { + super(); + datefield = parent; + setStyleName(VDateField.CLASSNAME + "-time"); + } + + private void buildTime(boolean redraw) { + final boolean thc = datefield.getDateTimeService().isTwelveHourClock(); + if (redraw) { + clear(); + final int numHours = thc ? 12 : 24; + hours = new ListBox(); + hours.setStyleName(VNativeSelect.CLASSNAME); + for (int i = 0; i < numHours; i++) { + hours.addItem((i < 10) ? "0" + i : "" + i); + } + hours.addChangeListener(this); + if (thc) { + ampm = new ListBox(); + ampm.setStyleName(VNativeSelect.CLASSNAME); + final String[] ampmText = datefield.getDateTimeService() + .getAmPmStrings(); + ampm.addItem(ampmText[0]); + ampm.addItem(ampmText[1]); + ampm.addChangeListener(this); + } + + if (datefield.getCurrentResolution() >= VDateField.RESOLUTION_MIN) { + mins = new ListBox(); + mins.setStyleName(VNativeSelect.CLASSNAME); + for (int i = 0; i < 60; i++) { + mins.addItem((i < 10) ? "0" + i : "" + i); + } + mins.addChangeListener(this); + } + if (datefield.getCurrentResolution() >= VDateField.RESOLUTION_SEC) { + sec = new ListBox(); + sec.setStyleName(VNativeSelect.CLASSNAME); + for (int i = 0; i < 60; i++) { + sec.addItem((i < 10) ? "0" + i : "" + i); + } + sec.addChangeListener(this); + } + if (datefield.getCurrentResolution() == VDateField.RESOLUTION_MSEC) { + msec = new ListBox(); + msec.setStyleName(VNativeSelect.CLASSNAME); + for (int i = 0; i < 1000; i++) { + if (i < 10) { + msec.addItem("00" + i); + } else if (i < 100) { + msec.addItem("0" + i); + } else { + msec.addItem("" + i); + } + } + msec.addChangeListener(this); + } + + final String delimiter = datefield.getDateTimeService() + .getClockDelimeter(); + final boolean ro = datefield.isReadonly(); + + if (ro) { + int h = 0; + if (datefield.getCurrentDate() != null) { + h = datefield.getCurrentDate().getHours(); + } + if (thc) { + h -= h < 12 ? 0 : 12; + } + add(new VLabel(h < 10 ? "0" + h : "" + h)); + } else { + add(hours); + } + + if (datefield.getCurrentResolution() >= VDateField.RESOLUTION_MIN) { + add(new VLabel(delimiter)); + if (ro) { + final int m = mins.getSelectedIndex(); + add(new VLabel(m < 10 ? "0" + m : "" + m)); + } else { + add(mins); + } + } + if (datefield.getCurrentResolution() >= VDateField.RESOLUTION_SEC) { + add(new VLabel(delimiter)); + if (ro) { + final int s = sec.getSelectedIndex(); + add(new VLabel(s < 10 ? "0" + s : "" + s)); + } else { + add(sec); + } + } + if (datefield.getCurrentResolution() == VDateField.RESOLUTION_MSEC) { + add(new VLabel(".")); + if (ro) { + final int m = datefield.getMilliseconds(); + final String ms = m < 100 ? "0" + m : "" + m; + add(new VLabel(m < 10 ? "0" + ms : ms)); + } else { + add(msec); + } + } + if (datefield.getCurrentResolution() == VDateField.RESOLUTION_HOUR) { + add(new VLabel(delimiter + "00")); // o'clock + } + if (thc) { + add(new VLabel(" ")); + if (ro) { + add(new VLabel(ampm.getItemText(datefield.getCurrentDate() + .getHours() < 12 ? 0 : 1))); + } else { + add(ampm); + } + } + + if (ro) { + return; + } + } + + // Update times + Date cdate = datefield.getCurrentDate(); + boolean selected = true; + if (cdate == null) { + cdate = new Date(); + selected = false; + } + if (thc) { + int h = cdate.getHours(); + ampm.setSelectedIndex(h < 12 ? 0 : 1); + h -= ampm.getSelectedIndex() * 12; + hours.setSelectedIndex(h); + } else { + hours.setSelectedIndex(cdate.getHours()); + } + if (datefield.getCurrentResolution() >= VDateField.RESOLUTION_MIN) { + mins.setSelectedIndex(cdate.getMinutes()); + } + if (datefield.getCurrentResolution() >= VDateField.RESOLUTION_SEC) { + sec.setSelectedIndex(cdate.getSeconds()); + } + if (datefield.getCurrentResolution() == VDateField.RESOLUTION_MSEC) { + if (selected) { + msec.setSelectedIndex(datefield.getMilliseconds()); + } else { + msec.setSelectedIndex(0); + } + } + if (thc) { + ampm.setSelectedIndex(cdate.getHours() < 12 ? 0 : 1); + } + + if (datefield.isReadonly() && !redraw) { + // Do complete redraw when in read-only status + clear(); + final String delimiter = datefield.getDateTimeService() + .getClockDelimeter(); + + int h = cdate.getHours(); + if (thc) { + h -= h < 12 ? 0 : 12; + } + add(new VLabel(h < 10 ? "0" + h : "" + h)); + + if (datefield.getCurrentResolution() >= VDateField.RESOLUTION_MIN) { + add(new VLabel(delimiter)); + final int m = mins.getSelectedIndex(); + add(new VLabel(m < 10 ? "0" + m : "" + m)); + } + if (datefield.getCurrentResolution() >= VDateField.RESOLUTION_SEC) { + add(new VLabel(delimiter)); + final int s = sec.getSelectedIndex(); + add(new VLabel(s < 10 ? "0" + s : "" + s)); + } + if (datefield.getCurrentResolution() == VDateField.RESOLUTION_MSEC) { + add(new VLabel(".")); + final int m = datefield.getMilliseconds(); + final String ms = m < 100 ? "0" + m : "" + m; + add(new VLabel(m < 10 ? "0" + ms : ms)); + } + if (datefield.getCurrentResolution() == VDateField.RESOLUTION_HOUR) { + add(new VLabel(delimiter + "00")); // o'clock + } + if (thc) { + add(new VLabel(" ")); + add(new VLabel(ampm.getItemText(cdate.getHours() < 12 ? 0 : 1))); + } + } + + final boolean enabled = datefield.isEnabled(); + hours.setEnabled(enabled); + if (mins != null) { + mins.setEnabled(enabled); + } + if (sec != null) { + sec.setEnabled(enabled); + } + if (msec != null) { + msec.setEnabled(enabled); + } + if (ampm != null) { + ampm.setEnabled(enabled); + } + + } + + public void updateTime(boolean redraw) { + buildTime(redraw || resolution != datefield.getCurrentResolution() + || readonly != datefield.isReadonly()); + if (datefield instanceof VTextualDate) { + ((VTextualDate) datefield).buildDate(); + } + resolution = datefield.getCurrentResolution(); + readonly = datefield.isReadonly(); + } + + public void onChange(Widget sender) { + if (datefield.getCurrentDate() == null) { + // was null on server, need to set + Date now = datefield.getShowingDate(); + if (now == null) { + now = new Date(); + datefield.setShowingDate(now); + } + datefield.setCurrentDate(new Date(now.getTime())); + + // Init variables with current time + datefield.getClient().updateVariable(datefield.getId(), "year", + now.getYear() + 1900, false); + datefield.getClient().updateVariable(datefield.getId(), "month", + now.getMonth() + 1, false); + datefield.getClient().updateVariable(datefield.getId(), "day", + now.getDate(), false); + datefield.getClient().updateVariable(datefield.getId(), "hour", + now.getHours(), false); + datefield.getClient().updateVariable(datefield.getId(), "min", + now.getMinutes(), false); + datefield.getClient().updateVariable(datefield.getId(), "sec", + now.getSeconds(), false); + datefield.getClient().updateVariable(datefield.getId(), "msec", + datefield.getMilliseconds(), false); + } + if (sender == hours) { + int h = hours.getSelectedIndex(); + if (datefield.getDateTimeService().isTwelveHourClock()) { + h = h + ampm.getSelectedIndex() * 12; + } + datefield.getCurrentDate().setHours(h); + datefield.getShowingDate().setHours(h); + datefield.getClient().updateVariable(datefield.getId(), "hour", h, + datefield.isImmediate()); + updateTime(false); + } else if (sender == mins) { + final int m = mins.getSelectedIndex(); + datefield.getCurrentDate().setMinutes(m); + datefield.getShowingDate().setMinutes(m); + datefield.getClient().updateVariable(datefield.getId(), "min", m, + datefield.isImmediate()); + updateTime(false); + } else if (sender == sec) { + final int s = sec.getSelectedIndex(); + datefield.getCurrentDate().setSeconds(s); + datefield.getShowingDate().setSeconds(s); + datefield.getClient().updateVariable(datefield.getId(), "sec", s, + datefield.isImmediate()); + updateTime(false); + } else if (sender == msec) { + final int ms = msec.getSelectedIndex(); + datefield.setMilliseconds(ms); + datefield.setShowingMilliseconds(ms); + datefield.getClient().updateVariable(datefield.getId(), "msec", ms, + datefield.isImmediate()); + updateTime(false); + } else if (sender == ampm) { + final int h = hours.getSelectedIndex() + ampm.getSelectedIndex() + * 12; + datefield.getCurrentDate().setHours(h); + datefield.getShowingDate().setHours(h); + datefield.getClient().updateVariable(datefield.getId(), "hour", h, + datefield.isImmediate()); + updateTime(false); + } + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VToolkitOverlay.java b/src/com/vaadin/terminal/gwt/client/ui/VToolkitOverlay.java new file mode 100644 index 0000000000..9ea284b575 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VToolkitOverlay.java @@ -0,0 +1,313 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.animation.client.Animation; +import com.google.gwt.dom.client.Document; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.PopupListener; +import com.google.gwt.user.client.ui.PopupPanel; +import com.google.gwt.user.client.ui.RootPanel; +import com.vaadin.terminal.gwt.client.BrowserInfo; + +/** + * In Toolkit UI this Overlay should always be used for all elements that + * temporary float over other components like context menus etc. This is to deal + * stacking order correctly with VWindow objects. + */ +public class VToolkitOverlay extends PopupPanel { + + /* + * The z-index value from where all overlays live. This can be overridden in + * any extending class. + */ + protected static int Z_INDEX = 20000; + + /* + * Shadow element style. If an extending class wishes to use a different + * style of shadow, it can use setShadowStyle(String) to give the shadow + * element a new style name. + */ + public static final String CLASSNAME_SHADOW = "i-shadow"; + + /* + * The shadow element for this overlay. + */ + private Element shadow; + + /** + * The HTML snippet that is used to render the actual shadow. In consists of + * nine different DIV-elements with the following class names: + * + *
+     *   .i-shadow[-stylename]
+     *   ----------------------------------------------
+     *   | .top-left     |   .top    |     .top-right |
+     *   |---------------|-----------|----------------|
+     *   |               |           |                |
+     *   | .left         |  .center  |         .right |
+     *   |               |           |                |
+     *   |---------------|-----------|----------------|
+     *   | .bottom-left  |  .bottom  |  .bottom-right |
+     *   ----------------------------------------------
+     * 
+ * + * See default theme 'shadow.css' for implementation example. + */ + private static final String SHADOW_HTML = "
"; + + public VToolkitOverlay() { + super(); + adjustZIndex(); + } + + public VToolkitOverlay(boolean autoHide) { + super(autoHide); + adjustZIndex(); + } + + public VToolkitOverlay(boolean autoHide, boolean modal) { + super(autoHide, modal); + adjustZIndex(); + } + + public VToolkitOverlay(boolean autoHide, boolean modal, boolean showShadow) { + super(autoHide, modal); + if (showShadow) { + shadow = DOM.createDiv(); + shadow.setClassName(CLASSNAME_SHADOW); + shadow.setInnerHTML(SHADOW_HTML); + DOM.setStyleAttribute(shadow, "position", "absolute"); + + addPopupListener(new PopupListener() { + public void onPopupClosed(PopupPanel sender, boolean autoClosed) { + if (shadow.getParentElement() != null) { + shadow.getParentElement().removeChild(shadow); + } + } + }); + } + adjustZIndex(); + } + + private void adjustZIndex() { + setZIndex(Z_INDEX); + } + + /** + * Set the z-index (visual stack position) for this overlay. + * + * @param zIndex + * The new z-index + */ + protected void setZIndex(int zIndex) { + DOM.setStyleAttribute(getElement(), "zIndex", "" + zIndex); + if (shadow != null) { + DOM.setStyleAttribute(shadow, "zIndex", "" + zIndex); + } + if (BrowserInfo.get().isIE6()) { + adjustIE6Frame(getElement(), zIndex - 1); + } + } + + /** + * Get the z-index (visual stack position) of this overlay. + * + * @return The z-index for this overlay. + */ + private int getZIndex() { + return Integer.parseInt(DOM.getStyleAttribute(getElement(), "zIndex")); + } + + @Override + public void setPopupPosition(int left, int top) { + super.setPopupPosition(left, top); + if (shadow != null) { + updateShadowSizeAndPosition(isAnimationEnabled() ? 0 : 1); + } + } + + @Override + public void show() { + super.show(); + if (shadow != null) { + if (isAnimationEnabled()) { + ShadowAnimation sa = new ShadowAnimation(); + sa.run(200); + } else { + updateShadowSizeAndPosition(1.0); + } + } + if (BrowserInfo.get().isIE6()) { + adjustIE6Frame(getElement(), getZIndex()); + } + } + + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + if (shadow != null) { + shadow.getStyle().setProperty("visibility", + visible ? "visible" : "hidden"); + } + } + + /* + * Needed to position overlays on top of native SELECT elements in IE6. See + * bug #2004 + */ + private native void adjustIE6Frame(Element popup, int zindex) + /*-{ + // relies on PopupImplIE6 + if(popup.__frame) + popup.__frame.style.zIndex = zindex; + }-*/; + + @Override + public void setWidth(String width) { + super.setWidth(width); + if (shadow != null) { + updateShadowSizeAndPosition(1.0); + } + } + + @Override + public void setHeight(String height) { + super.setHeight(height); + if (shadow != null) { + updateShadowSizeAndPosition(1.0); + } + } + + /** + * Sets the shadow style for this overlay. Will override any previous style + * for the shadow. The default style name is defined by CLASSNAME_SHADOW. + * The given style will be prefixed with CLASSNAME_SHADOW. + * + * @param style + * The new style name for the shadow element. Will be prefixed by + * CLASSNAME_SHADOW, e.g. style=='foobar' -> actual style + * name=='i-shadow-foobar'. + */ + protected void setShadowStyle(String style) { + if (shadow != null) { + shadow.setClassName(CLASSNAME_SHADOW + "-" + style); + } + } + + /* + * Extending classes should always call this method after they change the + * size of overlay without using normal 'setWidth(String)' and + * 'setHeight(String)' methods (if not calling super.setWidth/Height). + */ + protected void updateShadowSizeAndPosition() { + updateShadowSizeAndPosition(1.0); + } + + /** + * Recalculates proper position and dimensions for the shadow element. Can + * be used to animate the shadow, using the 'progress' parameter (used to + * animate the shadow in sync with GWT PopupPanel's default animation + * 'PopupPanel.AnimationType.CENTER'). + * + * @param progress + * A value between 0.0 and 1.0, indicating the progress of the + * animation (0=start, 1=end). + */ + private void updateShadowSizeAndPosition(final double progress) { + // Don't do anything if overlay element is not attached + if (!isAttached()) { + return; + } + // Calculate proper z-index + String zIndex = null; + try { + // Odd behaviour with Windows Hosted Mode forces us to use + // this redundant try/catch block (See dev.itmill.com #2011) + zIndex = DOM.getStyleAttribute(getElement(), "zIndex"); + } catch (Exception ignore) { + // Ignored, will cause no harm + } + if (zIndex == null) { + zIndex = "" + Z_INDEX; + } + // Calculate position and size + if (BrowserInfo.get().isIE()) { + // Shake IE + getOffsetHeight(); + getOffsetWidth(); + } + + int x = getAbsoluteLeft(); + int y = getAbsoluteTop(); + + /* This is needed for IE7 at least */ + // Account for the difference between absolute position and the + // body's positioning context. + x -= Document.get().getBodyOffsetLeft(); + y -= Document.get().getBodyOffsetTop(); + + int width = getOffsetWidth(); + int height = getOffsetHeight(); + + if (width < 0) { + width = 0; + } + if (height < 0) { + height = 0; + } + + // Animate the shadow size + x += (int) (width * (1.0 - progress) / 2.0); + y += (int) (height * (1.0 - progress) / 2.0); + width = (int) (width * progress); + height = (int) (height * progress); + + // Opera needs some shaking to get parts of the shadow showing + // properly + // (ticket #2704) + if (BrowserInfo.get().isOpera()) { + // Clear the height of all middle elements + DOM.getChild(shadow, 3).getStyle().setProperty("height", "auto"); + DOM.getChild(shadow, 4).getStyle().setProperty("height", "auto"); + DOM.getChild(shadow, 5).getStyle().setProperty("height", "auto"); + } + + // Update correct values + DOM.setStyleAttribute(shadow, "zIndex", zIndex); + DOM.setStyleAttribute(shadow, "width", width + "px"); + DOM.setStyleAttribute(shadow, "height", height + "px"); + DOM.setStyleAttribute(shadow, "top", y + "px"); + DOM.setStyleAttribute(shadow, "left", x + "px"); + DOM.setStyleAttribute(shadow, "display", progress < 0.9 ? "none" : ""); + + // Opera fix, part 2 (ticket #2704) + if (BrowserInfo.get().isOpera()) { + // We'll fix the height of all the middle elements + DOM.getChild(shadow, 3).getStyle().setPropertyPx("height", + DOM.getChild(shadow, 3).getOffsetHeight()); + DOM.getChild(shadow, 4).getStyle().setPropertyPx("height", + DOM.getChild(shadow, 4).getOffsetHeight()); + DOM.getChild(shadow, 5).getStyle().setPropertyPx("height", + DOM.getChild(shadow, 5).getOffsetHeight()); + } + + // Attach to dom if not there already + if (shadow.getParentElement() == null) { + RootPanel.get().getElement().insertBefore(shadow, getElement()); + } + + } + + protected class ShadowAnimation extends Animation { + @Override + protected void onUpdate(double progress) { + if (shadow != null) { + updateShadowSizeAndPosition(progress); + } + } + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTree.java b/src/com/vaadin/terminal/gwt/client/ui/VTree.java new file mode 100644 index 0000000000..57887bb019 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VTree.java @@ -0,0 +1,469 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.SimplePanel; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.MouseEventDetails; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +/** + * + */ +public class VTree extends FlowPanel implements Paintable { + + public static final String CLASSNAME = "i-tree"; + + private Set selectedIds = new HashSet(); + private ApplicationConnection client; + private String paintableId; + private boolean selectable; + private boolean isMultiselect; + + private final HashMap keyToNode = new HashMap(); + + /** + * This map contains captions and icon urls for actions like: * "33_c" -> + * "Edit" * "33_i" -> "http://dom.com/edit.png" + */ + private final HashMap actionMap = new HashMap(); + + private boolean immediate; + + private boolean isNullSelectionAllowed = true; + + private boolean disabled = false; + + private boolean readonly; + + private boolean emitClickEvents; + + private boolean rendering; + + public VTree() { + super(); + setStyleName(CLASSNAME); + } + + private void updateActionMap(UIDL c) { + final Iterator it = c.getChildIterator(); + while (it.hasNext()) { + final UIDL action = (UIDL) it.next(); + final String key = action.getStringAttribute("key"); + final String caption = action.getStringAttribute("caption"); + actionMap.put(key + "_c", caption); + if (action.hasAttribute("icon")) { + // TODO need some uri handling ?? + actionMap.put(key + "_i", client.translateToolkitUri(action + .getStringAttribute("icon"))); + } + } + + } + + public String getActionCaption(String actionKey) { + return actionMap.get(actionKey + "_c"); + } + + public String getActionIcon(String actionKey) { + return actionMap.get(actionKey + "_i"); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + // Ensure correct implementation and let container manage caption + if (client.updateComponent(this, uidl, true)) { + return; + } + + rendering = true; + + this.client = client; + + if (uidl.hasAttribute("partialUpdate")) { + handleUpdate(uidl); + rendering = false; + return; + } + + paintableId = uidl.getId(); + + immediate = uidl.hasAttribute("immediate"); + + disabled = uidl.getBooleanAttribute("disabled"); + readonly = uidl.getBooleanAttribute("readonly"); + emitClickEvents = uidl.getBooleanAttribute("listenClicks"); + + isNullSelectionAllowed = uidl.getBooleanAttribute("nullselect"); + + clear(); + for (final Iterator i = uidl.getChildIterator(); i.hasNext();) { + final UIDL childUidl = (UIDL) i.next(); + if ("actions".equals(childUidl.getTag())) { + updateActionMap(childUidl); + continue; + } + final TreeNode childTree = new TreeNode(); + this.add(childTree); + childTree.updateFromUIDL(childUidl, client); + } + final String selectMode = uidl.getStringAttribute("selectmode"); + selectable = !"none".equals(selectMode); + isMultiselect = "multi".equals(selectMode); + + selectedIds = uidl.getStringArrayVariableAsSet("selected"); + + rendering = false; + + } + + private void handleUpdate(UIDL uidl) { + final TreeNode rootNode = keyToNode.get(uidl + .getStringAttribute("rootKey")); + if (rootNode != null) { + if (!rootNode.getState()) { + // expanding node happened server side + rootNode.setState(true, false); + } + rootNode.renderChildNodes(uidl.getChildIterator()); + } + + if (uidl.hasVariable("selected")) { + // update selection in case selected nodes were not visible + selectedIds = uidl.getStringArrayVariableAsSet("selected"); + } + + } + + public void setSelected(TreeNode treeNode, boolean selected) { + if (selected) { + if (!isMultiselect) { + while (selectedIds.size() > 0) { + final String id = selectedIds.iterator().next(); + final TreeNode oldSelection = keyToNode.get(id); + if (oldSelection != null) { + // can be null if the node is not visible (parent + // collapsed) + oldSelection.setSelected(false); + } + selectedIds.remove(id); + } + } + treeNode.setSelected(true); + selectedIds.add(treeNode.key); + } else { + if (!isNullSelectionAllowed) { + if (!isMultiselect || selectedIds.size() == 1) { + return; + } + } + selectedIds.remove(treeNode.key); + treeNode.setSelected(false); + } + client.updateVariable(paintableId, "selected", selectedIds.toArray(), + immediate); + } + + public boolean isSelected(TreeNode treeNode) { + return selectedIds.contains(treeNode.key); + } + + protected class TreeNode extends SimplePanel implements ActionOwner { + + public static final String CLASSNAME = "i-tree-node"; + + String key; + + private String[] actionKeys = null; + + private boolean childrenLoaded; + + private Element nodeCaptionDiv; + + protected Element nodeCaptionSpan; + + private FlowPanel childNodeContainer; + + private boolean open; + + private Icon icon; + + private Element ie6compatnode; + + public TreeNode() { + constructDom(); + sinkEvents(Event.ONCLICK | Event.ONDBLCLICK | Event.ONMOUSEUP + | Event.ONCONTEXTMENU); + } + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + if (disabled) { + return; + } + final int type = DOM.eventGetType(event); + final Element target = DOM.eventGetTarget(event); + if (emitClickEvents && target == nodeCaptionSpan + && (type == Event.ONDBLCLICK || type == Event.ONMOUSEUP)) { + fireClick(event); + } + if (type == Event.ONCLICK) { + if (getElement() == target || ie6compatnode == target) { + // state change + toggleState(); + } else if (!readonly && target == nodeCaptionSpan) { + // caption click = selection change && possible click event + toggleSelection(); + } + DOM.eventCancelBubble(event, true); + } else if (type == Event.ONCONTEXTMENU) { + showContextMenu(event); + } + } + + private void fireClick(Event evt) { + // non-immediate iff an immediate select event is going to happen + boolean imm = !immediate + || !selectable + || (!isNullSelectionAllowed && isSelected() && selectedIds + .size() == 1); + MouseEventDetails details = new MouseEventDetails(evt); + client.updateVariable(paintableId, "clickedKey", key, false); + client.updateVariable(paintableId, "clickEvent", + details.toString(), imm); + } + + private void toggleSelection() { + if (selectable) { + VTree.this.setSelected(this, !isSelected()); + } + } + + private void toggleState() { + setState(!getState(), true); + } + + protected void constructDom() { + // workaround for a very weird IE6 issue #1245 + ie6compatnode = DOM.createDiv(); + setStyleName(ie6compatnode, CLASSNAME + "-ie6compatnode"); + DOM.setInnerText(ie6compatnode, " "); + DOM.appendChild(getElement(), ie6compatnode); + + DOM.sinkEvents(ie6compatnode, Event.ONCLICK); + + nodeCaptionDiv = DOM.createDiv(); + DOM.setElementProperty(nodeCaptionDiv, "className", CLASSNAME + + "-caption"); + Element wrapper = DOM.createDiv(); + nodeCaptionSpan = DOM.createSpan(); + DOM.appendChild(getElement(), nodeCaptionDiv); + DOM.appendChild(nodeCaptionDiv, wrapper); + DOM.appendChild(wrapper, nodeCaptionSpan); + + childNodeContainer = new FlowPanel(); + childNodeContainer.setStylePrimaryName(CLASSNAME + "-children"); + setWidget(childNodeContainer); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + setText(uidl.getStringAttribute("caption")); + key = uidl.getStringAttribute("key"); + + keyToNode.put(key, this); + + if (uidl.hasAttribute("al")) { + actionKeys = uidl.getStringArrayAttribute("al"); + } + + if (uidl.getTag().equals("node")) { + if (uidl.getChildCount() == 0) { + childNodeContainer.setVisible(false); + } else { + renderChildNodes(uidl.getChildIterator()); + childrenLoaded = true; + } + } else { + addStyleName(CLASSNAME + "-leaf"); + } + addStyleName(CLASSNAME); + + if (uidl.getBooleanAttribute("expanded") && !getState()) { + setState(true, false); + } + + if (uidl.getBooleanAttribute("selected")) { + setSelected(true); + } + + if (uidl.hasAttribute("icon")) { + if (icon == null) { + icon = new Icon(client); + DOM.insertBefore(DOM.getFirstChild(nodeCaptionDiv), icon + .getElement(), nodeCaptionSpan); + } + icon.setUri(uidl.getStringAttribute("icon")); + } else { + if (icon != null) { + DOM.removeChild(DOM.getFirstChild(nodeCaptionDiv), icon + .getElement()); + icon = null; + } + } + + if (BrowserInfo.get().isIE6() && isAttached()) { + fixWidth(); + } + } + + private void setState(boolean state, boolean notifyServer) { + if (open == state) { + return; + } + if (state) { + if (!childrenLoaded && notifyServer) { + client.updateVariable(paintableId, "requestChildTree", + true, false); + } + if (notifyServer) { + client.updateVariable(paintableId, "expand", + new String[] { key }, true); + } + addStyleName(CLASSNAME + "-expanded"); + childNodeContainer.setVisible(true); + + } else { + removeStyleName(CLASSNAME + "-expanded"); + childNodeContainer.setVisible(false); + if (notifyServer) { + client.updateVariable(paintableId, "collapse", + new String[] { key }, true); + } + } + open = state; + + if (!rendering) { + Util.notifyParentOfSizeChange(VTree.this, false); + } + } + + private boolean getState() { + return open; + } + + private void setText(String text) { + DOM.setInnerText(nodeCaptionSpan, text); + } + + private void renderChildNodes(Iterator i) { + childNodeContainer.clear(); + childNodeContainer.setVisible(true); + while (i.hasNext()) { + final UIDL childUidl = (UIDL) i.next(); + // actions are in bit weird place, don't mix them with children, + // but current node's actions + if ("actions".equals(childUidl.getTag())) { + updateActionMap(childUidl); + continue; + } + final TreeNode childTree = new TreeNode(); + childNodeContainer.add(childTree); + childTree.updateFromUIDL(childUidl, client); + } + childrenLoaded = true; + } + + public boolean isChildrenLoaded() { + return childrenLoaded; + } + + public Action[] getActions() { + if (actionKeys == null) { + return new Action[] {}; + } + final Action[] actions = new Action[actionKeys.length]; + for (int i = 0; i < actions.length; i++) { + final String actionKey = actionKeys[i]; + final TreeAction a = new TreeAction(this, String.valueOf(key), + actionKey); + a.setCaption(getActionCaption(actionKey)); + a.setIconUrl(getActionIcon(actionKey)); + actions[i] = a; + } + return actions; + } + + public ApplicationConnection getClient() { + return client; + } + + public String getPaintableId() { + return paintableId; + } + + /** + * Adds/removes IT Mill Toolkit specific style name. This method ought + * to be called only from VTree. + * + * @param selected + */ + protected void setSelected(boolean selected) { + // add style name to caption dom structure only, not to subtree + setStyleName(nodeCaptionDiv, "i-tree-node-selected", selected); + } + + protected boolean isSelected() { + return VTree.this.isSelected(this); + } + + public void showContextMenu(Event event) { + if (!readonly && !disabled) { + if (actionKeys != null) { + int left = event.getClientX(); + int top = event.getClientY(); + top += Window.getScrollTop(); + left += Window.getScrollLeft(); + client.getContextMenu().showAt(this, left, top); + } + event.cancelBubble(true); + event.preventDefault(); + } + } + + /* + * We need to fix the width of TreeNodes so that the float in + * ie6compatNode does not wrap (see ticket #1245) + */ + private void fixWidth() { + nodeCaptionDiv.getStyle().setProperty("styleFloat", "left"); + nodeCaptionDiv.getStyle().setProperty("display", "inline"); + nodeCaptionDiv.getStyle().setProperty("marginLeft", "0"); + final int captionWidth = ie6compatnode.getOffsetWidth() + + nodeCaptionDiv.getOffsetWidth(); + setWidth(captionWidth + "px"); + } + + @Override + public void onAttach() { + super.onAttach(); + if (BrowserInfo.get().isIE6()) { + fixWidth(); + } + } + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTwinColSelect.java b/src/com/vaadin/terminal/gwt/client/ui/VTwinColSelect.java new file mode 100644 index 0000000000..a439b38c43 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VTwinColSelect.java @@ -0,0 +1,244 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.Iterator; +import java.util.Vector; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.ListBox; +import com.google.gwt.user.client.ui.Panel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.UIDL; + +public class VTwinColSelect extends VOptionGroupBase { + + private static final String CLASSNAME = "i-select-twincol"; + + private static final int VISIBLE_COUNT = 10; + + private static final int DEFAULT_COLUMN_COUNT = 10; + + private final ListBox options; + + private final ListBox selections; + + private final VButton add; + + private final VButton remove; + + private FlowPanel buttons; + + private Panel panel; + + private boolean widthSet = false; + + public VTwinColSelect() { + super(CLASSNAME); + options = new ListBox(); + options.addClickListener(this); + selections = new ListBox(); + selections.addClickListener(this); + options.setVisibleItemCount(VISIBLE_COUNT); + selections.setVisibleItemCount(VISIBLE_COUNT); + options.setStyleName(CLASSNAME + "-options"); + selections.setStyleName(CLASSNAME + "-selections"); + buttons = new FlowPanel(); + buttons.setStyleName(CLASSNAME + "-buttons"); + add = new VButton(); + add.setText(">>"); + add.addClickListener(this); + remove = new VButton(); + remove.setText("<<"); + remove.addClickListener(this); + panel = ((Panel) optionsContainer); + panel.add(options); + buttons.add(add); + final HTML br = new HTML(""); + br.setStyleName(CLASSNAME + "-deco"); + buttons.add(br); + buttons.add(remove); + panel.add(buttons); + panel.add(selections); + } + + @Override + protected void buildOptions(UIDL uidl) { + final boolean enabled = !isDisabled() && !isReadonly(); + options.setMultipleSelect(isMultiselect()); + selections.setMultipleSelect(isMultiselect()); + options.setEnabled(enabled); + selections.setEnabled(enabled); + add.setEnabled(enabled); + remove.setEnabled(enabled); + options.clear(); + selections.clear(); + for (final Iterator i = uidl.getChildIterator(); i.hasNext();) { + final UIDL optionUidl = (UIDL) i.next(); + if (optionUidl.hasAttribute("selected")) { + selections.addItem(optionUidl.getStringAttribute("caption"), + optionUidl.getStringAttribute("key")); + } else { + options.addItem(optionUidl.getStringAttribute("caption"), + optionUidl.getStringAttribute("key")); + } + } + + int cols = -1; + if (getColumns() > 0) { + cols = getColumns(); + } else if (!widthSet) { + cols = DEFAULT_COLUMN_COUNT; + } + + if (cols >= 0) { + options.setWidth(cols + "em"); + selections.setWidth(cols + "em"); + buttons.setWidth("3.5em"); + optionsContainer.setWidth((2 * cols + 4) + "em"); + } + if (getRows() > 0) { + options.setVisibleItemCount(getRows()); + selections.setVisibleItemCount(getRows()); + + } + + } + + @Override + protected Object[] getSelectedItems() { + final Vector selectedItemKeys = new Vector(); + for (int i = 0; i < selections.getItemCount(); i++) { + selectedItemKeys.add(selections.getValue(i)); + } + return selectedItemKeys.toArray(); + } + + private boolean[] getItemsToAdd() { + final boolean[] selectedIndexes = new boolean[options.getItemCount()]; + for (int i = 0; i < options.getItemCount(); i++) { + if (options.isItemSelected(i)) { + selectedIndexes[i] = true; + } else { + selectedIndexes[i] = false; + } + } + return selectedIndexes; + } + + private boolean[] getItemsToRemove() { + final boolean[] selectedIndexes = new boolean[selections.getItemCount()]; + for (int i = 0; i < selections.getItemCount(); i++) { + if (selections.isItemSelected(i)) { + selectedIndexes[i] = true; + } else { + selectedIndexes[i] = false; + } + } + return selectedIndexes; + } + + @Override + public void onClick(Widget sender) { + super.onClick(sender); + if (sender == add) { + final boolean[] sel = getItemsToAdd(); + for (int i = 0; i < sel.length; i++) { + if (sel[i]) { + final int optionIndex = i + - (sel.length - options.getItemCount()); + selectedKeys.add(options.getValue(optionIndex)); + + // Move selection to another column + final String text = options.getItemText(optionIndex); + final String value = options.getValue(optionIndex); + selections.addItem(text, value); + selections.setItemSelected(selections.getItemCount() - 1, + true); + options.removeItem(optionIndex); + } + } + client.updateVariable(id, "selected", selectedKeys.toArray(), + isImmediate()); + + } else if (sender == remove) { + final boolean[] sel = getItemsToRemove(); + for (int i = 0; i < sel.length; i++) { + if (sel[i]) { + final int selectionIndex = i + - (sel.length - selections.getItemCount()); + selectedKeys.remove(selections.getValue(selectionIndex)); + + // Move selection to another column + final String text = selections.getItemText(selectionIndex); + final String value = selections.getValue(selectionIndex); + options.addItem(text, value); + options.setItemSelected(options.getItemCount() - 1, true); + selections.removeItem(selectionIndex); + } + } + client.updateVariable(id, "selected", selectedKeys.toArray(), + isImmediate()); + } else if (sender == options) { + // unselect all in other list, to avoid mistakes (i.e wrong button) + final int c = selections.getItemCount(); + for (int i = 0; i < c; i++) { + selections.setItemSelected(i, false); + } + } else if (sender == selections) { + // unselect all in other list, to avoid mistakes (i.e wrong button) + final int c = options.getItemCount(); + for (int i = 0; i < c; i++) { + options.setItemSelected(i, false); + } + } + } + + @Override + public void setHeight(String height) { + super.setHeight(height); + if ("".equals(height)) { + options.setHeight(""); + selections.setHeight(""); + } else { + setFullHeightInternals(); + } + } + + private void setFullHeightInternals() { + options.setHeight("100%"); + selections.setHeight("100%"); + } + + @Override + public void setWidth(String width) { + super.setWidth(width); + if (!"".equals(width) && width != null) { + setRelativeInternalWidths(); + } + } + + private void setRelativeInternalWidths() { + DOM.setStyleAttribute(getElement(), "position", "relative"); + buttons.setWidth("15%"); + options.setWidth("42%"); + selections.setWidth("42%"); + widthSet = true; + } + + @Override + protected void setTabIndex(int tabIndex) { + options.setTabIndex(tabIndex); + selections.setTabIndex(tabIndex); + add.setTabIndex(tabIndex); + remove.setTabIndex(tabIndex); + } + + public void focus() { + options.setFocus(true); + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VUnknownComponent.java b/src/com/vaadin/terminal/gwt/client/ui/VUnknownComponent.java new file mode 100644 index 0000000000..12faa71992 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VUnknownComponent.java @@ -0,0 +1,40 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.Tree; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; + +public class VUnknownComponent extends Composite implements Paintable { + + com.google.gwt.user.client.ui.Label caption = new com.google.gwt.user.client.ui.Label();; + Tree uidlTree = new Tree(); + + public VUnknownComponent() { + final VerticalPanel panel = new VerticalPanel(); + panel.add(caption); + panel.add(uidlTree); + initWidget(panel); + setStyleName("itmill-unknown"); + caption.setStyleName("itmill-unknown-caption"); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + if (client.updateComponent(this, uidl, false)) { + return; + } + setCaption("Client faced an unknown component type. Unrendered UIDL:"); + uidlTree.clear(); + uidlTree.addItem(uidl.dir()); + } + + public void setCaption(String c) { + caption.setText(c); + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VUpload.java b/src/com/vaadin/terminal/gwt/client/ui/VUpload.java new file mode 100644 index 0000000000..0010bda5c1 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VUpload.java @@ -0,0 +1,152 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.ClickListener; +import com.google.gwt.user.client.ui.FileUpload; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.FormHandler; +import com.google.gwt.user.client.ui.FormPanel; +import com.google.gwt.user.client.ui.FormSubmitCompleteEvent; +import com.google.gwt.user.client.ui.FormSubmitEvent; +import com.google.gwt.user.client.ui.Panel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; + +public class VUpload extends FormPanel implements Paintable, ClickListener, + FormHandler { + + public static final String CLASSNAME = "i-upload"; + + /** + * FileUpload component that opens native OS dialog to select file. + */ + FileUpload fu = new FileUpload(); + + Panel panel = new FlowPanel(); + + ApplicationConnection client; + + private String paintableId; + + /** + * Button that initiates uploading + */ + private final Button submitButton; + + /** + * When expecting big files, programmer may initiate some UI changes when + * uploading the file starts. Bit after submitting file we'll visit the + * server to check possible changes. + */ + private Timer t; + + /** + * some browsers tries to send form twice if submit is called in button + * click handler, some don't submit at all without it, so we need to track + * if form is already being submitted + */ + private boolean submitted = false; + + private boolean enabled = true; + + public VUpload() { + super(); + setEncoding(FormPanel.ENCODING_MULTIPART); + setMethod(FormPanel.METHOD_POST); + + setWidget(panel); + panel.add(fu); + submitButton = new Button(); + submitButton.addClickListener(this); + panel.add(submitButton); + + addFormHandler(this); + + setStyleName(CLASSNAME); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + if (client.updateComponent(this, uidl, true)) { + return; + } + this.client = client; + paintableId = uidl.getId(); + setAction(client.getAppUri()); + submitButton.setText(uidl.getStringAttribute("buttoncaption")); + fu.setName(paintableId + "_file"); + + if (uidl.hasAttribute("disabled") || uidl.hasAttribute("readonly")) { + disableUpload(); + } else if (uidl.getBooleanAttribute("state")) { + enableUploaod(); + } + + } + + public void onClick(Widget sender) { + submit(); + } + + public void onSubmit(FormSubmitEvent event) { + if (fu.getFilename().length() == 0 || submitted || !enabled) { + event.setCancelled(true); + ApplicationConnection + .getConsole() + .log( + "Submit cancelled (disabled, no file or already submitted)"); + return; + } + // flush possibly pending variable changes, so they will be handled + // before upload + client.sendPendingVariableChanges(); + + submitted = true; + ApplicationConnection.getConsole().log("Submitted form"); + + disableUpload(); + + /* + * Visit server a moment after upload has started to see possible + * changes from UploadStarted event. Will be cleared on complete. + */ + t = new Timer() { + @Override + public void run() { + client.sendPendingVariableChanges(); + } + }; + t.schedule(800); + } + + protected void disableUpload() { + submitButton.setEnabled(false); + fu.setVisible(false); + enabled = false; + } + + protected void enableUploaod() { + submitButton.setEnabled(true); + fu.setVisible(true); + enabled = true; + } + + public void onSubmitComplete(FormSubmitCompleteEvent event) { + if (client != null) { + if (t != null) { + t.cancel(); + } + ApplicationConnection.getConsole().log("Submit complete"); + client.sendPendingVariableChanges(); + } + submitted = false; + enableUploaod(); + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VUriFragmentUtility.java b/src/com/vaadin/terminal/gwt/client/ui/VUriFragmentUtility.java new file mode 100644 index 0000000000..2b53711cc1 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VUriFragmentUtility.java @@ -0,0 +1,67 @@ +package com.vaadin.terminal.gwt.client.ui; + +import com.google.gwt.dom.client.Document; +import com.google.gwt.user.client.History; +import com.google.gwt.user.client.HistoryListener; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; + +/** + * Client side implementation for UriFragmentUtility. Uses GWT's History object + * as an implementation. + * + */ +public class VUriFragmentUtility extends Widget implements Paintable, + HistoryListener { + + private String fragment; + private ApplicationConnection client; + private String paintableId; + private boolean immediate; + + public VUriFragmentUtility() { + setElement(Document.get().createDivElement()); + if (BrowserInfo.get().isIE6()) { + getElement().getStyle().setProperty("overflow", "hidden"); + getElement().getStyle().setProperty("height", "0"); + } + History.addHistoryListener(this); + History.fireCurrentHistoryState(); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + if (client.updateComponent(this, uidl, false)) { + return; + } + String uidlFragment = uidl.getStringVariable("fragment"); + immediate = uidl.getBooleanAttribute("immediate"); + if (this.client == null) { + // initial paint has some special logic + this.client = client; + paintableId = uidl.getId(); + if (!fragment.equals(uidlFragment)) { + // initial server side fragment (from link/bookmark/typed) does + // not equal the one on + // server, send initial fragment to server + History.fireCurrentHistoryState(); + } + } else { + if (uidlFragment != null && !uidlFragment.equals(fragment)) { + fragment = uidlFragment; + // normal fragment change from server, add new history item + History.newItem(uidlFragment, false); + } + } + } + + public void onHistoryChanged(String historyToken) { + fragment = historyToken; + if (client != null) { + client.updateVariable(paintableId, "fragment", fragment, immediate); + } + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VVerticalLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VVerticalLayout.java new file mode 100644 index 0000000000..c6b944fc4b --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VVerticalLayout.java @@ -0,0 +1,11 @@ +package com.vaadin.terminal.gwt.client.ui; + +public class VVerticalLayout extends VOrderedLayout { + + public static final String CLASSNAME = "i-verticallayout"; + + public VVerticalLayout() { + super(CLASSNAME, ORIENTATION_VERTICAL); + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VView.java b/src/com/vaadin/terminal/gwt/client/ui/VView.java new file mode 100644 index 0000000000..a9aaf403b7 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VView.java @@ -0,0 +1,575 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Set; + +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.Document; +import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.DeferredCommand; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.WindowCloseListener; +import com.google.gwt.user.client.WindowResizeListener; +import com.google.gwt.user.client.ui.HasFocus; +import com.google.gwt.user.client.ui.RootPanel; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.Container; +import com.vaadin.terminal.gwt.client.Focusable; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.RenderSpace; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +/** + * + */ +public class VView extends SimplePanel implements Container, + WindowResizeListener, WindowCloseListener { + + private static final String CLASSNAME = "i-view"; + + private String theme; + + private Paintable layout; + + private final LinkedHashSet subWindows = new LinkedHashSet(); + + private String id; + + private ShortcutActionHandler actionHandler; + + /** stored width for IE resize optimization */ + private int width; + + /** stored height for IE resize optimization */ + private int height; + + private ApplicationConnection connection; + + /** + * We are postponing resize process with IE. IE bugs with scrollbars in some + * situations, that causes false onWindowResized calls. With Timer we will + * give IE some time to decide if it really wants to keep current size + * (scrollbars). + */ + private Timer resizeTimer; + + private int scrollTop; + + private int scrollLeft; + + private boolean rendering; + + private boolean scrollable; + + private boolean immediate; + + public VView(String elementId) { + super(); + setStyleName(CLASSNAME); + + DOM.sinkEvents(getElement(), Event.ONKEYDOWN | Event.ONSCROLL); + + // iview is focused when created so element needs tabIndex + // 1 due 0 is at the end of natural tabbing order + DOM.setElementProperty(getElement(), "tabIndex", "1"); + + RootPanel root = RootPanel.get(elementId); + root.add(this); + root.removeStyleName("i-app-loading"); + + BrowserInfo browser = BrowserInfo.get(); + + // set focus to iview element by default to listen possible keyboard + // shortcuts + if (browser.isOpera() || browser.isSafari() + && browser.getWebkitVersion() < 526) { + // old webkits don't support focusing div elements + Element fElem = DOM.createInputCheck(); + DOM.setStyleAttribute(fElem, "margin", "0"); + DOM.setStyleAttribute(fElem, "padding", "0"); + DOM.setStyleAttribute(fElem, "border", "0"); + DOM.setStyleAttribute(fElem, "outline", "0"); + DOM.setStyleAttribute(fElem, "width", "1px"); + DOM.setStyleAttribute(fElem, "height", "1px"); + DOM.setStyleAttribute(fElem, "position", "absolute"); + DOM.setStyleAttribute(fElem, "opacity", "0.1"); + DOM.appendChild(getElement(), fElem); + focus(fElem); + } else { + focus(getElement()); + } + + } + + private static native void focus(Element el) + /*-{ + try { + el.focus(); + } catch (e) { + + } + }-*/; + + public String getTheme() { + return theme; + } + + /** + * Used to reload host page on theme changes. + */ + private static native void reloadHostPage() + /*-{ + $wnd.location.reload(); + }-*/; + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + rendering = true; + + id = uidl.getId(); + boolean firstPaint = connection == null; + connection = client; + + immediate = uidl.hasAttribute("immediate"); + + String newTheme = uidl.getStringAttribute("theme"); + if (theme != null && !newTheme.equals(theme)) { + // Complete page refresh is needed due css can affect layout + // calculations etc + reloadHostPage(); + } else { + theme = newTheme; + } + if (uidl.hasAttribute("style")) { + addStyleName(uidl.getStringAttribute("style")); + } + + if (uidl.hasAttribute("name")) { + client.setWindowName(uidl.getStringAttribute("name")); + } + + com.google.gwt.user.client.Window.setTitle(uidl + .getStringAttribute("caption")); + + // Process children + int childIndex = 0; + + // Open URL:s + boolean isClosed = false; // was this window closed? + while (childIndex < uidl.getChildCount() + && "open".equals(uidl.getChildUIDL(childIndex).getTag())) { + final UIDL open = uidl.getChildUIDL(childIndex); + final String url = open.getStringAttribute("src"); + final String target = open.getStringAttribute("name"); + if (target == null) { + // This window is closing. Send close event before + // going to the new url + isClosed = true; + onWindowClosed(); + goTo(url); + } else { + String options; + if (open.hasAttribute("border")) { + if (open.getStringAttribute("border").equals("minimal")) { + options = "menubar=yes,location=no,status=no"; + } else { + options = "menubar=no,location=no,status=no"; + } + + } else { + options = "resizable=yes,menubar=yes,toolbar=yes,directories=yes,location=yes,scrollbars=yes,status=yes"; + } + + if (open.hasAttribute("width")) { + int w = open.getIntAttribute("width"); + options += ",width=" + w; + } + if (open.hasAttribute("height")) { + int h = open.getIntAttribute("height"); + options += ",height=" + h; + } + + Window.open(url, target, options); + } + childIndex++; + } + if (isClosed) { + // don't render the content + rendering = false; + return; + } + + // Draw this application level window + UIDL childUidl = uidl.getChildUIDL(childIndex); + final Paintable lo = client.getPaintable(childUidl); + + if (layout != null) { + if (layout != lo) { + // remove old + client.unregisterPaintable(layout); + // add new + setWidget((Widget) lo); + layout = lo; + } + } else { + setWidget((Widget) lo); + layout = lo; + } + + layout.updateFromUIDL(childUidl, client); + + // Update subwindows + final HashSet removedSubWindows = new HashSet( + subWindows); + + // Open new windows + while ((childUidl = uidl.getChildUIDL(childIndex++)) != null) { + if ("window".equals(childUidl.getTag())) { + final Paintable w = client.getPaintable(childUidl); + if (subWindows.contains(w)) { + removedSubWindows.remove(w); + } else { + subWindows.add((VWindow) w); + } + w.updateFromUIDL(childUidl, client); + } else if ("actions".equals(childUidl.getTag())) { + if (actionHandler == null) { + actionHandler = new ShortcutActionHandler(id, client); + } + actionHandler.updateActionMap(childUidl); + } else if (childUidl.getTag().equals("notifications")) { + for (final Iterator it = childUidl.getChildIterator(); it + .hasNext();) { + final UIDL notification = (UIDL) it.next(); + String html = ""; + if (notification.hasAttribute("icon")) { + final String parsedUri = client + .translateToolkitUri(notification + .getStringAttribute("icon")); + html += ""; + } + if (notification.hasAttribute("caption")) { + html += "

" + + notification.getStringAttribute("caption") + + "

"; + } + if (notification.hasAttribute("message")) { + html += "

" + + notification.getStringAttribute("message") + + "

"; + } + + final String style = notification.hasAttribute("style") ? notification + .getStringAttribute("style") + : null; + final int position = notification + .getIntAttribute("position"); + final int delay = notification.getIntAttribute("delay"); + new VNotification(delay).show(html, position, style); + } + } + } + + // Close old windows + for (final Iterator rem = removedSubWindows.iterator(); rem + .hasNext();) { + final VWindow w = rem.next(); + client.unregisterPaintable(w); + subWindows.remove(w); + w.hide(); + } + + if (uidl.hasAttribute("focused")) { + final String focusPid = uidl.getStringAttribute("focused"); + // set focused component when render phase is finished + DeferredCommand.addCommand(new Command() { + public void execute() { + final Paintable toBeFocused = connection + .getPaintable(focusPid); + + /* + * Two types of Widgets can be focused, either implementing + * GWT HasFocus of a thinner Toolkit specific Focusable + * interface. + */ + if (toBeFocused instanceof HasFocus) { + final HasFocus toBeFocusedWidget = (HasFocus) toBeFocused; + toBeFocusedWidget.setFocus(true); + } else if (toBeFocused instanceof Focusable) { + ((Focusable) toBeFocused).focus(); + } else { + ApplicationConnection.getConsole().log( + "Could not focus component"); + } + } + }); + } + + // Add window listeners on first paint, to prevent premature + // variablechanges + if (firstPaint) { + Window.addWindowCloseListener(this); + Window.addWindowResizeListener(this); + } + + onWindowResized(Window.getClientWidth(), Window.getClientHeight()); + + if (BrowserInfo.get().isSafari()) { + Util.runWebkitOverflowAutoFix(getElement()); + } + + // finally set scroll position from UIDL + if (uidl.hasVariable("scrollTop")) { + scrollable = true; + scrollTop = uidl.getIntVariable("scrollTop"); + DOM.setElementPropertyInt(getElement(), "scrollTop", scrollTop); + scrollLeft = uidl.getIntVariable("scrollLeft"); + DOM.setElementPropertyInt(getElement(), "scrollLeft", scrollLeft); + } else { + scrollable = false; + } + + rendering = false; + } + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + int type = DOM.eventGetType(event); + if (type == Event.ONKEYDOWN && actionHandler != null) { + actionHandler.handleKeyboardEvent(event); + return; + } else if (scrollable && type == Event.ONSCROLL) { + updateScrollPosition(); + } + } + + /** + * Updates scroll position from DOM and saves variables to server. + */ + private void updateScrollPosition() { + int oldTop = scrollTop; + int oldLeft = scrollLeft; + scrollTop = DOM.getElementPropertyInt(getElement(), "scrollTop"); + scrollLeft = DOM.getElementPropertyInt(getElement(), "scrollLeft"); + if (connection != null && !rendering) { + if (oldTop != scrollTop) { + connection.updateVariable(id, "scrollTop", scrollTop, false); + } + if (oldLeft != scrollLeft) { + connection.updateVariable(id, "scrollLeft", scrollLeft, false); + } + } + } + + public void onWindowResized(int width, int height) { + if (BrowserInfo.get().isIE()) { + /* + * IE will give us some false resized events due bugs with + * scrollbars. Postponing layout phase to see if size was really + * changed. + */ + if (resizeTimer == null) { + resizeTimer = new Timer() { + @Override + public void run() { + boolean changed = false; + if (VView.this.width != getOffsetWidth()) { + VView.this.width = getOffsetWidth(); + changed = true; + ApplicationConnection.getConsole().log( + "window w" + VView.this.width); + } + if (VView.this.height != getOffsetHeight()) { + VView.this.height = getOffsetHeight(); + changed = true; + ApplicationConnection.getConsole().log( + "window h" + VView.this.height); + } + if (changed) { + ApplicationConnection + .getConsole() + .log( + "Running layout functions due window resize"); + connection.runDescendentsLayout(VView.this); + + sendClientResized(); + } + } + }; + } else { + resizeTimer.cancel(); + } + resizeTimer.schedule(200); + } else { + if (width == VView.this.width && height == VView.this.height) { + // No point in doing resize operations if window size has not + // changed + return; + } + + VView.this.width = Window.getClientWidth(); + VView.this.height = Window.getClientHeight(); + + ApplicationConnection.getConsole().log( + "Running layout functions due window resize"); + + connection.runDescendentsLayout(this); + Util.runWebkitOverflowAutoFix(getElement()); + + sendClientResized(); + } + + } + + /** + * Send new dimensions to the server. + */ + private void sendClientResized() { + connection.updateVariable(id, "height", height, false); + connection.updateVariable(id, "width", width, immediate); + } + + public native static void goTo(String url) + /*-{ + $wnd.location = url; + }-*/; + + public void onWindowClosed() { + // Change focus on this window in order to ensure that all state is + // collected from textfields + VTextField.flushChangesFromFocusedTextField(); + + // Send the closing state to server + connection.updateVariable(id, "close", true, false); + connection.sendPendingVariableChangesSync(); + } + + public String onWindowClosing() { + return null; + } + + private final RenderSpace myRenderSpace = new RenderSpace() { + private int excessHeight = -1; + private int excessWidth = -1; + + @Override + public int getHeight() { + return getElement().getOffsetHeight() - getExcessHeight(); + } + + private int getExcessHeight() { + if (excessHeight < 0) { + detectExcessSize(); + } + return excessHeight; + } + + private void detectExcessSize() { + // TODO define that iview cannot be themed and decorations should + // get to parent element, then get rid of this expensive and error + // prone function + final String overflow = getElement().getStyle().getProperty( + "overflow"); + getElement().getStyle().setProperty("overflow", "hidden"); + if (BrowserInfo.get().isIE() + && getElement().getPropertyInt("clientWidth") == 0) { + // can't detect possibly themed border/padding width in some + // situations (with some layout configurations), use empty div + // to measure width properly + DivElement div = Document.get().createDivElement(); + div.setInnerHTML(" "); + div.getStyle().setProperty("overflow", "hidden"); + div.getStyle().setProperty("height", "1px"); + getElement().appendChild(div); + excessWidth = getElement().getOffsetWidth() + - div.getOffsetWidth(); + getElement().removeChild(div); + } else { + excessWidth = getElement().getOffsetWidth() + - getElement().getPropertyInt("clientWidth"); + } + excessHeight = getElement().getOffsetHeight() + - getElement().getPropertyInt("clientHeight"); + + getElement().getStyle().setProperty("overflow", overflow); + } + + @Override + public int getWidth() { + return getElement().getOffsetWidth() - getExcessWidth(); + } + + private int getExcessWidth() { + if (excessWidth < 0) { + detectExcessSize(); + } + return excessWidth; + } + + @Override + public int getScrollbarSize() { + return Util.getNativeScrollbarSize(); + } + }; + + public RenderSpace getAllocatedSpace(Widget child) { + return myRenderSpace; + } + + public boolean hasChildComponent(Widget component) { + return (component != null && component == layout); + } + + public void replaceChildComponent(Widget oldComponent, Widget newComponent) { + // TODO This is untested as no layouts require this + if (oldComponent != layout) { + return; + } + + setWidget(newComponent); + layout = (Paintable) newComponent; + } + + public boolean requestLayout(Set child) { + /* + * Can never propagate further and we do not want need to re-layout the + * layout which has caused this request. + */ + return true; + + } + + public void updateCaption(Paintable component, UIDL uidl) { + // NOP Subwindows never draw caption for their first child (layout) + } + + /** + * Return an iterator for current subwindows. This method is meant for + * testing purposes only. + * + * @return + */ + public ArrayList getSubWindowList() { + ArrayList windows = new ArrayList(subWindows.size()); + for (VWindow widget : subWindows) { + windows.add(widget); + } + return windows; + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VWindow.java b/src/com/vaadin/terminal/gwt/client/ui/VWindow.java new file mode 100644 index 0000000000..18a7b6a623 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VWindow.java @@ -0,0 +1,1001 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.Iterator; +import java.util.Set; +import java.util.Vector; + +import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.DeferredCommand; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.ui.Frame; +import com.google.gwt.user.client.ui.HasWidgets; +import com.google.gwt.user.client.ui.RootPanel; +import com.google.gwt.user.client.ui.ScrollListener; +import com.google.gwt.user.client.ui.ScrollPanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.Container; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.RenderSpace; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; + +/** + * "Sub window" component. + * + * TODO update position / scroll position / size to client + * + * @author IT Mill Ltd + */ +public class VWindow extends VToolkitOverlay implements Container, + ScrollListener { + + private static final int MIN_HEIGHT = 100; + + private static final int MIN_WIDTH = 150; + + private static Vector windowOrder = new Vector(); + + public static final String CLASSNAME = "i-window"; + + /** + * Pixels used by inner borders and paddings horizontally (calculated only + * once) + */ + private int borderWidth = -1; + + /** + * Pixels used by inner borders and paddings vertically (calculated only + * once) + */ + private int borderHeight = -1; + + private static final int STACKING_OFFSET_PIXELS = 15; + + public static final int Z_INDEX = 10000; + + private Paintable layout; + + private Element contents; + + private Element header; + + private Element footer; + + private Element resizeBox; + + private final ScrollPanel contentPanel = new ScrollPanel(); + + private boolean dragging; + + private int startX; + + private int startY; + + private int origX; + + private int origY; + + private boolean resizing; + + private int origW; + + private int origH; + + private Element closeBox; + + protected ApplicationConnection client; + + private String id; + + ShortcutActionHandler shortcutHandler; + + /** Last known positionx read from UIDL or updated to application connection */ + private int uidlPositionX = -1; + + /** Last known positiony read from UIDL or updated to application connection */ + private int uidlPositionY = -1; + + private boolean vaadinModality = false; + + private boolean resizable = true; + + private Element modalityCurtain; + private Element draggingCurtain; + + private Element headerText; + + private boolean readonly; + + boolean dynamicWidth = false; + boolean dynamicHeight = false; + boolean layoutRelativeWidth = false; + boolean layoutRelativeHeight = false; + + // If centered (via UIDL), the window should stay in the centered -mode + // until a position is received from the server, or the user moves or + // resizes the window. + boolean centered = false; + + private RenderSpace renderSpace = new RenderSpace(MIN_WIDTH, MIN_HEIGHT, + true); + + private String width; + + private String height; + + private boolean immediate; + + public VWindow() { + super(false, false, true); // no autohide, not modal, shadow + // Different style of shadow for windows + setShadowStyle("window"); + + final int order = windowOrder.size(); + setWindowOrder(order); + windowOrder.add(this); + constructDOM(); + setPopupPosition(order * STACKING_OFFSET_PIXELS, order + * STACKING_OFFSET_PIXELS); + contentPanel.addScrollListener(this); + } + + private void bringToFront() { + int curIndex = windowOrder.indexOf(this); + if (curIndex + 1 < windowOrder.size()) { + windowOrder.remove(this); + windowOrder.add(this); + for (; curIndex < windowOrder.size(); curIndex++) { + windowOrder.get(curIndex).setWindowOrder(curIndex); + } + } + } + + /** + * Returns true if window is the topmost window + * + * @return + */ + private boolean isActive() { + return windowOrder.lastElement().equals(this); + } + + public void setWindowOrder(int order) { + setZIndex(order + Z_INDEX); + } + + @Override + protected void setZIndex(int zIndex) { + super.setZIndex(zIndex); + if (vaadinModality) { + DOM.setStyleAttribute(modalityCurtain, "zIndex", "" + zIndex); + } + } + + protected void constructDOM() { + setStyleName(CLASSNAME); + + header = DOM.createDiv(); + DOM.setElementProperty(header, "className", CLASSNAME + "-outerheader"); + headerText = DOM.createDiv(); + DOM.setElementProperty(headerText, "className", CLASSNAME + "-header"); + contents = DOM.createDiv(); + DOM.setElementProperty(contents, "className", CLASSNAME + "-contents"); + footer = DOM.createDiv(); + DOM.setElementProperty(footer, "className", CLASSNAME + "-footer"); + resizeBox = DOM.createDiv(); + DOM + .setElementProperty(resizeBox, "className", CLASSNAME + + "-resizebox"); + closeBox = DOM.createDiv(); + DOM.setElementProperty(closeBox, "className", CLASSNAME + "-closebox"); + DOM.appendChild(footer, resizeBox); + + DOM.sinkEvents(getElement(), Event.ONLOSECAPTURE); + DOM.sinkEvents(closeBox, Event.ONCLICK); + DOM.sinkEvents(contents, Event.ONCLICK); + + final Element wrapper = DOM.createDiv(); + DOM.setElementProperty(wrapper, "className", CLASSNAME + "-wrap"); + + final Element wrapper2 = DOM.createDiv(); + DOM.setElementProperty(wrapper2, "className", CLASSNAME + "-wrap2"); + + DOM.sinkEvents(wrapper, Event.ONKEYDOWN); + + DOM.appendChild(wrapper2, closeBox); + DOM.appendChild(wrapper2, header); + DOM.appendChild(header, headerText); + DOM.appendChild(wrapper2, contents); + DOM.appendChild(wrapper2, footer); + DOM.appendChild(wrapper, wrapper2); + DOM.appendChild(super.getContainerElement(), wrapper); + + sinkEvents(Event.MOUSEEVENTS); + + setWidget(contentPanel); + + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + id = uidl.getId(); + this.client = client; + + // Workaround needed for Testing Tools (GWT generates window DOM + // slightly different in different browsers). + DOM.setElementProperty(closeBox, "id", id + "_window_close"); + + if (uidl.hasAttribute("invisible")) { + hide(); + return; + } + + if (!uidl.hasAttribute("cached")) { + if (uidl.getBooleanAttribute("modal") != vaadinModality) { + setVaadinModality(!vaadinModality); + } + if (!isAttached()) { + show(); + } + } + + if (client.updateComponent(this, uidl, false)) { + return; + } + + immediate = uidl.hasAttribute("immediate"); + + if (uidl.getBooleanAttribute("resizable") != resizable) { + setResizable(!resizable); + } + + if (isReadOnly() != uidl.getBooleanAttribute("readonly")) { + setReadOnly(!isReadOnly()); + } + + // Initialize the position form UIDL + try { + final int positionx = uidl.getIntVariable("positionx"); + final int positiony = uidl.getIntVariable("positiony"); + if (positionx >= 0 && positiony >= 0) { + setPopupPosition(positionx, positiony); + } + } catch (final IllegalArgumentException e) { + // Silently ignored as positionx and positiony are not required + // parameters + } + + if (uidl.hasAttribute("caption")) { + setCaption(uidl.getStringAttribute("caption"), uidl + .getStringAttribute("icon")); + } + + boolean showingUrl = false; + int childIndex = 0; + UIDL childUidl = uidl.getChildUIDL(childIndex++); + while ("open".equals(childUidl.getTag())) { + // TODO multiple opens with the same target will in practice just + // open the last one - should we fix that somehow? + final String parsedUri = client.translateToolkitUri(childUidl + .getStringAttribute("src")); + if (!childUidl.hasAttribute("name")) { + final Frame frame = new Frame(); + DOM.setStyleAttribute(frame.getElement(), "width", "100%"); + DOM.setStyleAttribute(frame.getElement(), "height", "100%"); + DOM.setStyleAttribute(frame.getElement(), "border", "0px"); + frame.setUrl(parsedUri); + contentPanel.setWidget(frame); + showingUrl = true; + } else { + final String target = childUidl.getStringAttribute("name"); + Window.open(parsedUri, target, ""); + } + childUidl = uidl.getChildUIDL(childIndex++); + } + + final Paintable lo = client.getPaintable(childUidl); + if (layout != null) { + if (layout != lo) { + // remove old + client.unregisterPaintable(layout); + contentPanel.remove((Widget) layout); + // add new + if (!showingUrl) { + contentPanel.setWidget((Widget) lo); + } + layout = lo; + } + } else if (!showingUrl) { + contentPanel.setWidget((Widget) lo); + layout = lo; + } + + dynamicWidth = !uidl.hasAttribute("width"); + dynamicHeight = !uidl.hasAttribute("height"); + + layoutRelativeWidth = uidl.hasAttribute("layoutRelativeWidth"); + layoutRelativeHeight = uidl.hasAttribute("layoutRelativeHeight"); + + if (dynamicWidth && layoutRelativeWidth) { + /* + * Relative layout width, fix window width before rendering (width + * according to caption) + */ + setNaturalWidth(); + } + + layout.updateFromUIDL(childUidl, client); + if (!dynamicHeight && layoutRelativeWidth) { + /* + * Relative layout width, and fixed height. Must update the size to + * be able to take scrollbars into account (layout gets narrower + * space if it is higher than the window) -> only vertical scrollbar + */ + client.runDescendentsLayout(this); + } + + /* + * No explicit width is set and the layout does not have relative width + * so fix the size according to the layout. + */ + if (dynamicWidth && !layoutRelativeWidth) { + setNaturalWidth(); + } + + if (dynamicHeight && layoutRelativeHeight) { + // Prevent resizing until height has been fixed + resizable = false; + } + + // we may have actions and notifications + if (uidl.getChildCount() > 1) { + final int cnt = uidl.getChildCount(); + for (int i = 1; i < cnt; i++) { + childUidl = uidl.getChildUIDL(i); + if (childUidl.getTag().equals("actions")) { + if (shortcutHandler == null) { + shortcutHandler = new ShortcutActionHandler(id, client); + } + shortcutHandler.updateActionMap(childUidl); + } else if (childUidl.getTag().equals("notifications")) { + // TODO needed? move -> + for (final Iterator it = childUidl.getChildIterator(); it + .hasNext();) { + final UIDL notification = (UIDL) it.next(); + String html = ""; + if (notification.hasAttribute("icon")) { + final String parsedUri = client + .translateToolkitUri(notification + .getStringAttribute("icon")); + html += ""; + } + if (notification.hasAttribute("caption")) { + html += "

" + + notification + .getStringAttribute("caption") + + "

"; + } + if (notification.hasAttribute("message")) { + html += "

" + + notification + .getStringAttribute("message") + + "

"; + } + + final String style = notification.hasAttribute("style") ? notification + .getStringAttribute("style") + : null; + final int position = notification + .getIntAttribute("position"); + final int delay = notification.getIntAttribute("delay"); + new VNotification(delay).show(html, position, style); + } + } + } + + } + + // setting scrollposition must happen after children is rendered + contentPanel.setScrollPosition(uidl.getIntVariable("scrollTop")); + contentPanel.setHorizontalScrollPosition(uidl + .getIntVariable("scrollLeft")); + + // Center this window on screen if requested + // This has to be here because we might not know the content size before + // everything is painted into the window + if (uidl.getBooleanAttribute("center")) { + // mark as centered - this is unset on move/resize + centered = true; + center(); + } else { + // don't try to center the window anymore + centered = false; + } + + updateShadowSizeAndPosition(); + + // ensure window is not larger than browser window + if (getOffsetWidth() > Window.getClientWidth()) { + setWidth(Window.getClientWidth() + "px"); + } + if (getOffsetHeight() > Window.getClientHeight()) { + setHeight(Window.getClientHeight() + "px"); + } + + if (dynamicHeight && layoutRelativeHeight) { + /* + * Window height is undefined, layout is 100% high so the layout + * should define the initial window height but on resize the layout + * should be as high as the window. We fix the height to deal with + * this. + */ + + int h = contents.getOffsetHeight() + getExtraHeight(); + int w = contents.getOffsetWidth(); + + client.updateVariable(id, "height", h, false); + client.updateVariable(id, "width", w, true); + } + + } + + private void setNaturalWidth() { + /* + * For some reason IE6 has title DIV set to width 100% which messes this + * up. Also IE6 has a 0 wide element so we use the container element. + */ + int naturalWidth; + if (BrowserInfo.get().isIE6()) { + String headerW = headerText.getStyle().getProperty("width"); + headerText.getStyle().setProperty("width", "auto"); + naturalWidth = getElement().getOffsetWidth(); + headerText.getStyle().setProperty("width", headerW); + } else { + // use max(layout width, window width) + // i.e layout content width or caption width + int lowidth = contentPanel.getElement().getScrollWidth() + + borderWidth; // layout does not know about border + int elwidth = getElement().getOffsetWidth(); + naturalWidth = (lowidth > elwidth ? lowidth : elwidth); + } + + setWidth(naturalWidth + "px"); + } + + private void setReadOnly(boolean readonly) { + this.readonly = readonly; + if (readonly) { + DOM.setStyleAttribute(closeBox, "display", "none"); + } else { + DOM.setStyleAttribute(closeBox, "display", ""); + } + } + + private boolean isReadOnly() { + return readonly; + } + + @Override + public void show() { + if (vaadinModality) { + showModalityCurtain(); + } + super.show(); + + setFF2CaretFixEnabled(true); + fixFF3OverflowBug(); + } + + /** Disable overflow auto with FF3 to fix #1837. */ + private void fixFF3OverflowBug() { + if (BrowserInfo.get().isFF3()) { + DeferredCommand.addCommand(new Command() { + public void execute() { + DOM.setStyleAttribute(getElement(), "overflow", ""); + } + }); + } + } + + /** + * Fix "missing cursor" browser bug workaround for FF2 in Windows and Linux. + * + * Calling this method has no effect on other browsers than the ones based + * on Gecko 1.8 + * + * @param enable + */ + private void setFF2CaretFixEnabled(boolean enable) { + if (BrowserInfo.get().isFF2()) { + if (enable) { + DeferredCommand.addCommand(new Command() { + public void execute() { + DOM.setStyleAttribute(getElement(), "overflow", "auto"); + } + }); + } else { + DOM.setStyleAttribute(getElement(), "overflow", ""); + } + } + } + + @Override + public void hide() { + if (vaadinModality) { + hideModalityCurtain(); + } + super.hide(); + } + + private void setVaadinModality(boolean modality) { + vaadinModality = modality; + if (vaadinModality) { + modalityCurtain = DOM.createDiv(); + DOM.setElementProperty(modalityCurtain, "className", CLASSNAME + + "-modalitycurtain"); + if (isAttached()) { + showModalityCurtain(); + bringToFront(); + } else { + DeferredCommand.addCommand(new Command() { + public void execute() { + // vaadinModality window must on top of others + bringToFront(); + } + }); + } + } else { + if (modalityCurtain != null) { + if (isAttached()) { + hideModalityCurtain(); + } + modalityCurtain = null; + } + } + } + + private void showModalityCurtain() { + if (BrowserInfo.get().isFF2()) { + DOM.setStyleAttribute(modalityCurtain, "height", DOM + .getElementPropertyInt(RootPanel.getBodyElement(), + "offsetHeight") + + "px"); + DOM.setStyleAttribute(modalityCurtain, "position", "absolute"); + } + DOM.setStyleAttribute(modalityCurtain, "zIndex", "" + + (windowOrder.indexOf(this) + Z_INDEX)); + DOM.appendChild(RootPanel.getBodyElement(), modalityCurtain); + } + + private void hideModalityCurtain() { + DOM.removeChild(RootPanel.getBodyElement(), modalityCurtain); + } + + /* + * Shows (or hides) an empty div on top of all other content; used when + * resizing or moving, so that iframes (etc) do not steal event. + */ + private void showDraggingCurtain(boolean show) { + if (show && draggingCurtain == null) { + + setFF2CaretFixEnabled(false); // makes FF2 slow + + draggingCurtain = DOM.createDiv(); + DOM.setStyleAttribute(draggingCurtain, "position", "absolute"); + DOM.setStyleAttribute(draggingCurtain, "top", "0px"); + DOM.setStyleAttribute(draggingCurtain, "left", "0px"); + DOM.setStyleAttribute(draggingCurtain, "width", "100%"); + DOM.setStyleAttribute(draggingCurtain, "height", "100%"); + DOM.setStyleAttribute(draggingCurtain, "zIndex", "" + + VToolkitOverlay.Z_INDEX); + + DOM.appendChild(RootPanel.getBodyElement(), draggingCurtain); + } else if (!show && draggingCurtain != null) { + + setFF2CaretFixEnabled(true); // makes FF2 slow + + DOM.removeChild(RootPanel.getBodyElement(), draggingCurtain); + draggingCurtain = null; + } + + } + + private void setResizable(boolean resizability) { + resizable = resizability; + if (resizability) { + DOM.setElementProperty(resizeBox, "className", CLASSNAME + + "-resizebox"); + } else { + DOM.setElementProperty(resizeBox, "className", CLASSNAME + + "-resizebox " + CLASSNAME + "-resizebox-disabled"); + } + } + + @Override + public void setPopupPosition(int left, int top) { + super.setPopupPosition(left, top); + if (left != uidlPositionX && client != null) { + client.updateVariable(id, "positionx", left, false); + uidlPositionX = left; + } + if (top != uidlPositionY && client != null) { + client.updateVariable(id, "positiony", top, false); + uidlPositionY = top; + } + } + + public void setCaption(String c) { + setCaption(c, null); + } + + public void setCaption(String c, String icon) { + String html = Util.escapeHTML(c); + if (icon != null) { + icon = client.translateToolkitUri(icon); + html = "" + html; + } + DOM.setInnerHTML(headerText, html); + } + + @Override + protected Element getContainerElement() { + // in GWT 1.5 this method is used in PopupPanel constructor + if (contents == null) { + return super.getContainerElement(); + } + return contents; + } + + @Override + public void onBrowserEvent(final Event event) { + if (event != null) { + final int type = event.getTypeInt(); + + if (type == Event.ONKEYDOWN && shortcutHandler != null) { + shortcutHandler.handleKeyboardEvent(event); + return; + } + + final Element target = DOM.eventGetTarget(event); + + // Handle window caption tooltips + if (client != null && DOM.isOrHasChild(header, target)) { + client.handleTooltipEvent(event, this); + } + + if (resizing || resizeBox == target) { + onResizeEvent(event); + event.cancelBubble(true); + } else if (target == closeBox) { + if (type == Event.ONCLICK) { + onCloseClick(); + event.cancelBubble(true); + } + } else if (dragging || !DOM.isOrHasChild(contents, target)) { + onDragEvent(event); + event.cancelBubble(true); + } else if (type == Event.ONCLICK) { + // clicked inside window, ensure to be on top + if (!isActive()) { + bringToFront(); + } + } + } + } + + private void onCloseClick() { + client.updateVariable(id, "close", true, true); + } + + private void onResizeEvent(Event event) { + if (resizable) { + switch (event.getTypeInt()) { + case Event.ONMOUSEDOWN: + if (!isActive()) { + bringToFront(); + } + showDraggingCurtain(true); + if (BrowserInfo.get().isIE()) { + DOM.setStyleAttribute(resizeBox, "visibility", "hidden"); + } + resizing = true; + startX = event.getScreenX(); + startY = event.getScreenY(); + origW = getElement().getOffsetWidth(); + origH = getElement().getOffsetHeight(); + DOM.setCapture(getElement()); + event.preventDefault(); + break; + case Event.ONMOUSEUP: + showDraggingCurtain(false); + if (BrowserInfo.get().isIE()) { + DOM.setStyleAttribute(resizeBox, "visibility", ""); + } + resizing = false; + DOM.releaseCapture(getElement()); + setSize(event, true); + break; + case Event.ONLOSECAPTURE: + showDraggingCurtain(false); + if (BrowserInfo.get().isIE()) { + DOM.setStyleAttribute(resizeBox, "visibility", ""); + } + resizing = false; + case Event.ONMOUSEMOVE: + if (resizing) { + centered = false; + setSize(event, false); + event.preventDefault(); + } + break; + default: + event.preventDefault(); + break; + } + } + } + + private void setSize(Event event, boolean updateVariables) { + int w = event.getScreenX() - startX + origW; + if (w < MIN_WIDTH + borderWidth) { + w = MIN_WIDTH + borderWidth; + } + + int h = event.getScreenY() - startY + origH; + if (h < MIN_HEIGHT + getExtraHeight()) { + h = MIN_HEIGHT + getExtraHeight(); + } + + setWidth(w + "px"); + setHeight(h + "px"); + + if (updateVariables) { + // sending width back always as pixels, no need for unit + client.updateVariable(id, "width", w, false); + client.updateVariable(id, "height", h, immediate); + } + + // Update child widget dimensions + if (client != null) { + client.handleComponentRelativeSize((Widget) layout); + client.runDescendentsLayout((HasWidgets) layout); + } + + Util.runWebkitOverflowAutoFix(contentPanel.getElement()); + } + + @Override + /* + * Width is set to the out-most element (i-window). + * + * This function should never be called with percentage values (it will + * throw an exception) + */ + public void setWidth(String width) { + this.width = width; + if (!isAttached()) { + return; + } + if (width != null && !"".equals(width)) { + int pixelWidth; + // Convert non-pixel values to pixels + if (width.indexOf("px") < 0) { + DOM.setStyleAttribute(getElement(), "width", width); + pixelWidth = getElement().getOffsetWidth(); + width = pixelWidth + "px"; + } + if (BrowserInfo.get().isIE6()) { + getElement().getStyle().setProperty("overflow", "hidden"); + } + getElement().getStyle().setProperty("width", width); + + pixelWidth = getElement().getOffsetWidth() - borderWidth; + if (pixelWidth < MIN_WIDTH) { + pixelWidth = MIN_WIDTH; + int rootWidth = pixelWidth + borderWidth; + DOM.setStyleAttribute(getElement(), "width", rootWidth + "px"); + } + + renderSpace.setWidth(pixelWidth); + + // IE6 needs the actual inner content width on the content element, + // otherwise it won't wrap the content properly (no scrollbars + // appear, content flows out of window) + if (BrowserInfo.get().isIE6()) { + DOM.setStyleAttribute(contentPanel.getElement(), "width", + pixelWidth + "px"); + } + updateShadowSizeAndPosition(); + } + } + + @Override + /* + * Height is set to the out-most element (i-window). + * + * This function should never be called with percentage values (it will + * throw an exception) + */ + public void setHeight(String height) { + this.height = height; + if (!isAttached()) { + return; + } + if (height != null && !"".equals(height)) { + DOM.setStyleAttribute(getElement(), "height", height); + int pixels = getElement().getOffsetHeight() - getExtraHeight(); + if (pixels < MIN_HEIGHT) { + pixels = MIN_HEIGHT; + int rootHeight = pixels + getExtraHeight(); + DOM.setStyleAttribute(getElement(), "height", (rootHeight) + + "px"); + + } + renderSpace.setHeight(pixels); + height = pixels + "px"; + contentPanel.getElement().getStyle().setProperty("height", height); + updateShadowSizeAndPosition(); + + } + } + + private int extraH = 0; + + private int getExtraHeight() { + extraH = header.getOffsetHeight() + footer.getOffsetHeight(); + return extraH; + } + + private void onDragEvent(Event event) { + switch (DOM.eventGetType(event)) { + case Event.ONMOUSEDOWN: + if (!isActive()) { + bringToFront(); + } + showDraggingCurtain(true); + dragging = true; + startX = DOM.eventGetScreenX(event); + startY = DOM.eventGetScreenY(event); + origX = DOM.getAbsoluteLeft(getElement()); + origY = DOM.getAbsoluteTop(getElement()); + DOM.setCapture(getElement()); + DOM.eventPreventDefault(event); + break; + case Event.ONMOUSEUP: + dragging = false; + showDraggingCurtain(false); + DOM.releaseCapture(getElement()); + break; + case Event.ONLOSECAPTURE: + showDraggingCurtain(false); + dragging = false; + break; + case Event.ONMOUSEMOVE: + if (dragging) { + centered = false; + final int x = DOM.eventGetScreenX(event) - startX + origX; + final int y = DOM.eventGetScreenY(event) - startY + origY; + setPopupPosition(x, y); + DOM.eventPreventDefault(event); + } + break; + default: + break; + } + } + + @Override + public boolean onEventPreview(Event event) { + if (dragging) { + onDragEvent(event); + return false; + } else if (resizing) { + onResizeEvent(event); + return false; + } else if (vaadinModality) { + // return false when modal and outside window + final Element target = event.getTarget().cast(); + if (!DOM.isOrHasChild(getElement(), target)) { + return false; + } + } + return true; + } + + public void onScroll(Widget widget, int scrollLeft, int scrollTop) { + client.updateVariable(id, "scrollTop", scrollTop, false); + client.updateVariable(id, "scrollLeft", scrollLeft, false); + } + + @Override + public void addStyleDependentName(String styleSuffix) { + // VWindow's getStyleElement() does not return the same element as + // getElement(), so we need to override this. + setStyleName(getElement(), getStylePrimaryName() + "-" + styleSuffix, + true); + } + + @Override + protected void onAttach() { + super.onAttach(); + + // Calculate space required by window borders, so we can accurately + // calculate space for content + + // IE (IE6 especially) requires some magic tricks to pull the border + // size correctly (remember that we want to accomodate for paddings as + // well) + if (BrowserInfo.get().isIE()) { + DOM.setStyleAttribute(contents, "width", "7000px"); + DOM.setStyleAttribute(contentPanel.getElement(), "width", "7000px"); + int contentWidth = DOM.getElementPropertyInt(contentPanel + .getElement(), "offsetWidth"); + contentWidth = DOM.getElementPropertyInt(contentPanel.getElement(), + "offsetWidth"); + final int windowWidth = DOM.getElementPropertyInt(getElement(), + "offsetWidth"); + DOM.setStyleAttribute(contentPanel.getElement(), "width", ""); + DOM.setStyleAttribute(contents, "width", ""); + + borderWidth = windowWidth - contentWidth; + } + + // Standards based browsers get away with it a little easier :) + else { + final int contentWidth = DOM.getElementPropertyInt(contentPanel + .getElement(), "offsetWidth"); + final int windowWidth = DOM.getElementPropertyInt(getElement(), + "offsetWidth"); + borderWidth = windowWidth - contentWidth; + } + + setWidth(width); + setHeight(height); + + } + + public RenderSpace getAllocatedSpace(Widget child) { + if (child == layout) { + return renderSpace; + } else { + // Exception ?? + return null; + } + } + + public boolean hasChildComponent(Widget component) { + if (component == layout) { + return true; + } else { + return false; + } + } + + public void replaceChildComponent(Widget oldComponent, Widget newComponent) { + contentPanel.setWidget(newComponent); + } + + public boolean requestLayout(Set child) { + if (dynamicWidth && !layoutRelativeWidth) { + setNaturalWidth(); + } + if (centered) { + center(); + } + updateShadowSizeAndPosition(); + return true; + } + + public void updateCaption(Paintable component, UIDL uidl) { + // NOP, window has own caption, layout captio not rendered + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/layout/CellBasedLayout.java b/src/com/vaadin/terminal/gwt/client/ui/layout/CellBasedLayout.java index 5c2c2f0204..2da551dcdd 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/layout/CellBasedLayout.java +++ b/src/com/vaadin/terminal/gwt/client/ui/layout/CellBasedLayout.java @@ -13,7 +13,7 @@ import com.vaadin.terminal.gwt.client.BrowserInfo; import com.vaadin.terminal.gwt.client.Container; import com.vaadin.terminal.gwt.client.Paintable; import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.ui.IMarginInfo; +import com.vaadin.terminal.gwt.client.ui.VMarginInfo; public abstract class CellBasedLayout extends ComplexPanel implements Container { @@ -27,7 +27,7 @@ public abstract class CellBasedLayout extends ComplexPanel implements Container public static final int ORIENTATION_HORIZONTAL = 1; protected Margins activeMargins = new Margins(0, 0, 0, 0); - protected IMarginInfo activeMarginsInfo = new IMarginInfo(-1); + protected VMarginInfo activeMarginsInfo = new VMarginInfo(-1); protected boolean spacingEnabled = false; protected final Spacing spacingFromCSS = new Spacing(12, 12); @@ -214,7 +214,7 @@ public abstract class CellBasedLayout extends ComplexPanel implements Container private void updateMarginAndSpacingInfo(UIDL uidl) { int bitMask = uidl.getIntAttribute("margins"); if (activeMarginsInfo.getBitMask() != bitMask) { - activeMarginsInfo = new IMarginInfo(bitMask); + activeMarginsInfo = new VMarginInfo(bitMask); marginsNeedsRecalculation = true; } boolean spacing = uidl.getBooleanAttribute("spacing"); diff --git a/src/com/vaadin/terminal/gwt/client/ui/layout/ChildComponentContainer.java b/src/com/vaadin/terminal/gwt/client/ui/layout/ChildComponentContainer.java index 158138359d..299bbd6918 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/layout/ChildComponentContainer.java +++ b/src/com/vaadin/terminal/gwt/client/ui/layout/ChildComponentContainer.java @@ -12,7 +12,7 @@ import com.google.gwt.user.client.ui.Panel; import com.google.gwt.user.client.ui.Widget; import com.vaadin.terminal.gwt.client.ApplicationConnection; import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.ICaption; +import com.vaadin.terminal.gwt.client.VCaption; import com.vaadin.terminal.gwt.client.Paintable; import com.vaadin.terminal.gwt.client.UIDL; import com.vaadin.terminal.gwt.client.Util; @@ -62,7 +62,7 @@ public class ChildComponentContainer extends Panel { private int alignmentTopOffset = 0; // private Margins alignmentOffset = new Margins(0, 0, 0, 0); - private ICaption caption = null; + private VCaption caption = null; private DivElement containerDIV; private DivElement widgetDIV; private Widget widget; @@ -413,13 +413,13 @@ public class ChildComponentContainer extends Panel { } public void updateCaption(UIDL uidl, ApplicationConnection client) { - if (ICaption.isNeeded(uidl)) { + if (VCaption.isNeeded(uidl)) { // We need a caption - ICaption newCaption = caption; + VCaption newCaption = caption; if (newCaption == null) { - newCaption = new ICaption((Paintable) widget, client); + newCaption = new VCaption((Paintable) widget, client); // Set initial height to avoid Safari flicker newCaption.setHeight("18px"); // newCaption.setHeight(newCaption.getHeight()); // This might @@ -462,7 +462,7 @@ public class ChildComponentContainer extends Panel { } - private void setCaption(ICaption newCaption) { + private void setCaption(VCaption newCaption) { // Validate // if (newCaption == caption) { // return; diff --git a/src/com/vaadin/terminal/gwt/client/ui/richtextarea/IRichTextArea.java b/src/com/vaadin/terminal/gwt/client/ui/richtextarea/IRichTextArea.java deleted file mode 100644 index b6a3c85238..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/richtextarea/IRichTextArea.java +++ /dev/null @@ -1,251 +0,0 @@ -/* -@ITMillApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.client.ui.richtextarea; - -import com.google.gwt.user.client.Command; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.DeferredCommand; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.ui.ChangeListener; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.FocusListener; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.KeyboardListener; -import com.google.gwt.user.client.ui.RichTextArea; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.Paintable; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; -import com.vaadin.terminal.gwt.client.ui.Field; - -/** - * This class implements a basic client side rich text editor component. - * - * @author IT Mill Ltd. - * - */ -public class IRichTextArea extends Composite implements Paintable, Field, - ChangeListener, FocusListener, KeyboardListener { - - /** - * The input node CSS classname. - */ - public static final String CLASSNAME = "i-richtextarea"; - - protected String id; - - protected ApplicationConnection client; - - private boolean immediate = false; - - private RichTextArea rta = new RichTextArea(); - - private IRichTextToolbar formatter = new IRichTextToolbar(rta); - - private HTML html = new HTML(); - - private final FlowPanel fp = new FlowPanel(); - - private boolean enabled = true; - - private int extraHorizontalPixels = -1; - private int extraVerticalPixels = -1; - - private int maxLength = -1; - - private int toolbarNaturalWidth = 500; - - public IRichTextArea() { - fp.add(formatter); - - rta.setWidth("100%"); - rta.addFocusListener(this); - - fp.add(rta); - - initWidget(fp); - setStyleName(CLASSNAME); - - } - - public void setEnabled(boolean enabled) { - if (this.enabled != enabled) { - rta.setEnabled(enabled); - if (enabled) { - fp.remove(html); - fp.add(rta); - } else { - html.setHTML(rta.getHTML()); - fp.remove(rta); - fp.add(html); - } - - this.enabled = enabled; - } - } - - public void updateFromUIDL(final UIDL uidl, ApplicationConnection client) { - this.client = client; - id = uidl.getId(); - - if (uidl.hasVariable("text")) { - if (BrowserInfo.get().isIE()) { - // rta is rather buggy in IE (as pretty much everything is) - // it needs some "shaking" not to fall into uneditable state - // see #2374 - rta.getBasicFormatter().toggleBold(); - rta.getBasicFormatter().toggleBold(); - } - rta.setHTML(uidl.getStringVariable("text")); - - } - setEnabled(!uidl.getBooleanAttribute("disabled")); - - if (client.updateComponent(this, uidl, true)) { - return; - } - - immediate = uidl.getBooleanAttribute("immediate"); - int newMaxLength = uidl.hasAttribute("maxLength") ? uidl - .getIntAttribute("maxLength") : -1; - if (newMaxLength >= 0) { - if (maxLength == -1) { - rta.addKeyboardListener(this); - } - maxLength = newMaxLength; - } else if (maxLength != -1) { - getElement().setAttribute("maxlength", ""); - maxLength = -1; - rta.removeKeyboardListener(this); - } - } - - public void onChange(Widget sender) { - if (client != null && id != null) { - client.updateVariable(id, "text", rta.getText(), immediate); - } - } - - public void onFocus(Widget sender) { - - } - - public void onLostFocus(Widget sender) { - final String html = rta.getHTML(); - client.updateVariable(id, "text", html, immediate); - - } - - /** - * @return space used by components paddings and borders - */ - private int getExtraHorizontalPixels() { - if (extraHorizontalPixels < 0) { - detectExtraSizes(); - } - return extraHorizontalPixels; - } - - /** - * @return space used by components paddings and borders - */ - private int getExtraVerticalPixels() { - if (extraVerticalPixels < 0) { - detectExtraSizes(); - } - return extraVerticalPixels; - } - - /** - * Detects space used by components paddings and borders. - */ - private void detectExtraSizes() { - Element clone = Util.cloneNode(getElement(), false); - DOM.setElementAttribute(clone, "id", ""); - DOM.setStyleAttribute(clone, "visibility", "hidden"); - DOM.setStyleAttribute(clone, "position", "absolute"); - // due FF3 bug set size to 10px and later subtract it from extra pixels - DOM.setStyleAttribute(clone, "width", "10px"); - DOM.setStyleAttribute(clone, "height", "10px"); - DOM.appendChild(DOM.getParent(getElement()), clone); - extraHorizontalPixels = DOM.getElementPropertyInt(clone, "offsetWidth") - 10; - extraVerticalPixels = DOM.getElementPropertyInt(clone, "offsetHeight") - 10; - - DOM.removeChild(DOM.getParent(getElement()), clone); - } - - @Override - public void setHeight(String height) { - if (height.endsWith("px")) { - int h = Integer.parseInt(height.substring(0, height.length() - 2)); - h -= getExtraVerticalPixels(); - if (h < 0) { - h = 0; - } - - super.setHeight(h + "px"); - } else { - super.setHeight(height); - } - - if (height == null || height.equals("")) { - rta.setHeight(""); - } else { - int editorHeight = getOffsetHeight() - getExtraVerticalPixels() - - formatter.getOffsetHeight(); - rta.setHeight(editorHeight + "px"); - } - } - - @Override - public void setWidth(String width) { - if (width.endsWith("px")) { - int w = Integer.parseInt(width.substring(0, width.length() - 2)); - w -= getExtraHorizontalPixels(); - if (w < 0) { - w = 0; - } - - super.setWidth(w + "px"); - } else if (width.equals("")) { - /* - * IE cannot calculate the width of the 100% iframe correctly if - * there is no width specified for the parent. In this case we would - * use the toolbar but IE cannot calculate the width of that one - * correctly either in all cases. So we end up using a default width - * for a RichTextArea with no width definition in all browsers (for - * compatibility). - */ - - super.setWidth(toolbarNaturalWidth + "px"); - } else { - super.setWidth(width); - } - } - - public void onKeyDown(Widget sender, char keyCode, int modifiers) { - // NOP - } - - public void onKeyPress(Widget sender, char keyCode, int modifiers) { - if (maxLength >= 0) { - DeferredCommand.addCommand(new Command() { - public void execute() { - if (rta.getHTML().length() > maxLength) { - rta.setHTML(rta.getHTML().substring(0, maxLength)); - } - } - }); - } - } - - public void onKeyUp(Widget sender, char keyCode, int modifiers) { - // NOP - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/richtextarea/IRichTextToolbar$Strings.properties b/src/com/vaadin/terminal/gwt/client/ui/richtextarea/IRichTextToolbar$Strings.properties deleted file mode 100644 index 363b704584..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/richtextarea/IRichTextToolbar$Strings.properties +++ /dev/null @@ -1,35 +0,0 @@ -bold = Toggle Bold -createLink = Create Link -hr = Insert Horizontal Rule -indent = Indent Right -insertImage = Insert Image -italic = Toggle Italic -justifyCenter = Center -justifyLeft = Left Justify -justifyRight = Right Justify -ol = Insert Ordered List -outdent = Indent Left -removeFormat = Remove Formatting -removeLink = Remove Link -strikeThrough = Toggle Strikethrough -subscript = Toggle Subscript -superscript = Toggle Superscript -ul = Insert Unordered List -underline = Toggle Underline -color = Color -black = Black -white = White -red = Red -green = Green -yellow = Yellow -blue = Blue -font = Font -normal = Normal -size = Size -xxsmall = XX-Small -xsmall = X-Small -small = Small -medium = Medium -large = Large -xlarge = X-Large -xxlarge = XX-Large \ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/client/ui/richtextarea/IRichTextToolbar.java b/src/com/vaadin/terminal/gwt/client/ui/richtextarea/IRichTextToolbar.java deleted file mode 100644 index 433be77464..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/richtextarea/IRichTextToolbar.java +++ /dev/null @@ -1,474 +0,0 @@ -/* - * Copyright 2007 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.terminal.gwt.client.ui.richtextarea; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.i18n.client.Constants; -import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.ui.AbstractImagePrototype; -import com.google.gwt.user.client.ui.ChangeListener; -import com.google.gwt.user.client.ui.ClickListener; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.ImageBundle; -import com.google.gwt.user.client.ui.KeyboardListener; -import com.google.gwt.user.client.ui.ListBox; -import com.google.gwt.user.client.ui.PushButton; -import com.google.gwt.user.client.ui.RichTextArea; -import com.google.gwt.user.client.ui.ToggleButton; -import com.google.gwt.user.client.ui.Widget; - -/** - * A modified version of sample toolbar for use with {@link RichTextArea}. It - * provides a simple UI for all rich text formatting, dynamically displayed only - * for the available functionality. - */ -public class IRichTextToolbar extends Composite { - - /** - * This {@link ImageBundle} is used for all the button icons. Using an image - * bundle allows all of these images to be packed into a single image, which - * saves a lot of HTTP requests, drastically improving startup time. - */ - public interface Images extends ImageBundle { - - @ImageBundle.Resource("bold.gif") - AbstractImagePrototype bold(); - - @ImageBundle.Resource("createLink.gif") - AbstractImagePrototype createLink(); - - @ImageBundle.Resource("hr.gif") - AbstractImagePrototype hr(); - - @ImageBundle.Resource("indent.gif") - AbstractImagePrototype indent(); - - @ImageBundle.Resource("insertImage.gif") - AbstractImagePrototype insertImage(); - - @ImageBundle.Resource("italic.gif") - AbstractImagePrototype italic(); - - @ImageBundle.Resource("justifyCenter.gif") - AbstractImagePrototype justifyCenter(); - - @ImageBundle.Resource("justifyLeft.gif") - AbstractImagePrototype justifyLeft(); - - @ImageBundle.Resource("justifyRight.gif") - AbstractImagePrototype justifyRight(); - - @ImageBundle.Resource("ol.gif") - AbstractImagePrototype ol(); - - @ImageBundle.Resource("outdent.gif") - AbstractImagePrototype outdent(); - - @ImageBundle.Resource("removeFormat.gif") - AbstractImagePrototype removeFormat(); - - @ImageBundle.Resource("removeLink.gif") - AbstractImagePrototype removeLink(); - - @ImageBundle.Resource("strikeThrough.gif") - AbstractImagePrototype strikeThrough(); - - @ImageBundle.Resource("subscript.gif") - AbstractImagePrototype subscript(); - - @ImageBundle.Resource("superscript.gif") - AbstractImagePrototype superscript(); - - @ImageBundle.Resource("ul.gif") - AbstractImagePrototype ul(); - - @ImageBundle.Resource("underline.gif") - AbstractImagePrototype underline(); - } - - /** - * This {@link Constants} interface is used to make the toolbar's strings - * internationalizable. - */ - public interface Strings extends Constants { - - String black(); - - String blue(); - - String bold(); - - String color(); - - String createLink(); - - String font(); - - String green(); - - String hr(); - - String indent(); - - String insertImage(); - - String italic(); - - String justifyCenter(); - - String justifyLeft(); - - String justifyRight(); - - String large(); - - String medium(); - - String normal(); - - String ol(); - - String outdent(); - - String red(); - - String removeFormat(); - - String removeLink(); - - String size(); - - String small(); - - String strikeThrough(); - - String subscript(); - - String superscript(); - - String ul(); - - String underline(); - - String white(); - - String xlarge(); - - String xsmall(); - - String xxlarge(); - - String xxsmall(); - - String yellow(); - } - - /** - * We use an inner EventListener class to avoid exposing event methods on - * the RichTextToolbar itself. - */ - private class EventListener implements ClickListener, ChangeListener, - KeyboardListener { - - public void onChange(Widget sender) { - if (sender == backColors) { - basic.setBackColor(backColors.getValue(backColors - .getSelectedIndex())); - backColors.setSelectedIndex(0); - } else if (sender == foreColors) { - basic.setForeColor(foreColors.getValue(foreColors - .getSelectedIndex())); - foreColors.setSelectedIndex(0); - } else if (sender == fonts) { - basic.setFontName(fonts.getValue(fonts.getSelectedIndex())); - fonts.setSelectedIndex(0); - } else if (sender == fontSizes) { - basic.setFontSize(fontSizesConstants[fontSizes - .getSelectedIndex() - 1]); - fontSizes.setSelectedIndex(0); - } - } - - public void onClick(Widget sender) { - if (sender == bold) { - basic.toggleBold(); - } else if (sender == italic) { - basic.toggleItalic(); - } else if (sender == underline) { - basic.toggleUnderline(); - } else if (sender == subscript) { - basic.toggleSubscript(); - } else if (sender == superscript) { - basic.toggleSuperscript(); - } else if (sender == strikethrough) { - extended.toggleStrikethrough(); - } else if (sender == indent) { - extended.rightIndent(); - } else if (sender == outdent) { - extended.leftIndent(); - } else if (sender == justifyLeft) { - basic.setJustification(RichTextArea.Justification.LEFT); - } else if (sender == justifyCenter) { - basic.setJustification(RichTextArea.Justification.CENTER); - } else if (sender == justifyRight) { - basic.setJustification(RichTextArea.Justification.RIGHT); - } else if (sender == insertImage) { - final String url = Window.prompt("Enter an image URL:", - "http://"); - if (url != null) { - extended.insertImage(url); - } - } else if (sender == createLink) { - final String url = Window - .prompt("Enter a link URL:", "http://"); - if (url != null) { - extended.createLink(url); - } - } else if (sender == removeLink) { - extended.removeLink(); - } else if (sender == hr) { - extended.insertHorizontalRule(); - } else if (sender == ol) { - extended.insertOrderedList(); - } else if (sender == ul) { - extended.insertUnorderedList(); - } else if (sender == removeFormat) { - extended.removeFormat(); - } else if (sender == richText) { - // We use the RichTextArea's onKeyUp event to update the toolbar - // status. - // This will catch any cases where the user moves the cursur - // using the - // keyboard, or uses one of the browser's built-in keyboard - // shortcuts. - updateStatus(); - } - } - - public void onKeyDown(Widget sender, char keyCode, int modifiers) { - } - - public void onKeyPress(Widget sender, char keyCode, int modifiers) { - } - - public void onKeyUp(Widget sender, char keyCode, int modifiers) { - if (sender == richText) { - // We use the RichTextArea's onKeyUp event to update the toolbar - // status. - // This will catch any cases where the user moves the cursur - // using the - // keyboard, or uses one of the browser's built-in keyboard - // shortcuts. - updateStatus(); - } - } - } - - private static final RichTextArea.FontSize[] fontSizesConstants = new RichTextArea.FontSize[] { - RichTextArea.FontSize.XX_SMALL, RichTextArea.FontSize.X_SMALL, - RichTextArea.FontSize.SMALL, RichTextArea.FontSize.MEDIUM, - RichTextArea.FontSize.LARGE, RichTextArea.FontSize.X_LARGE, - RichTextArea.FontSize.XX_LARGE }; - - private final Images images = (Images) GWT.create(Images.class); - private final Strings strings = (Strings) GWT.create(Strings.class); - private final EventListener listener = new EventListener(); - - private final RichTextArea richText; - private final RichTextArea.BasicFormatter basic; - private final RichTextArea.ExtendedFormatter extended; - - private final FlowPanel outer = new FlowPanel(); - private final FlowPanel topPanel = new FlowPanel(); - private final FlowPanel bottomPanel = new FlowPanel(); - private ToggleButton bold; - private ToggleButton italic; - private ToggleButton underline; - private ToggleButton subscript; - private ToggleButton superscript; - private ToggleButton strikethrough; - private PushButton indent; - private PushButton outdent; - private PushButton justifyLeft; - private PushButton justifyCenter; - private PushButton justifyRight; - private PushButton hr; - private PushButton ol; - private PushButton ul; - private PushButton insertImage; - private PushButton createLink; - private PushButton removeLink; - private PushButton removeFormat; - - private ListBox backColors; - private ListBox foreColors; - private ListBox fonts; - private ListBox fontSizes; - - /** - * Creates a new toolbar that drives the given rich text area. - * - * @param richText - * the rich text area to be controlled - */ - public IRichTextToolbar(RichTextArea richText) { - this.richText = richText; - basic = richText.getBasicFormatter(); - extended = richText.getExtendedFormatter(); - - outer.add(topPanel); - outer.add(bottomPanel); - topPanel.setWidth("100%"); - topPanel.setHeight("20px"); - topPanel.getElement().getStyle().setProperty("overflow", "hidden"); - bottomPanel.setWidth("100%"); - - initWidget(outer); - setStyleName("gwt-RichTextToolbar"); - - if (basic != null) { - topPanel.add(bold = createToggleButton(images.bold(), strings - .bold())); - topPanel.add(italic = createToggleButton(images.italic(), strings - .italic())); - topPanel.add(underline = createToggleButton(images.underline(), - strings.underline())); - topPanel.add(subscript = createToggleButton(images.subscript(), - strings.subscript())); - topPanel.add(superscript = createToggleButton(images.superscript(), - strings.superscript())); - topPanel.add(justifyLeft = createPushButton(images.justifyLeft(), - strings.justifyLeft())); - topPanel.add(justifyCenter = createPushButton(images - .justifyCenter(), strings.justifyCenter())); - topPanel.add(justifyRight = createPushButton(images.justifyRight(), - strings.justifyRight())); - } - - if (extended != null) { - topPanel.add(strikethrough = createToggleButton(images - .strikeThrough(), strings.strikeThrough())); - topPanel.add(indent = createPushButton(images.indent(), strings - .indent())); - topPanel.add(outdent = createPushButton(images.outdent(), strings - .outdent())); - topPanel.add(hr = createPushButton(images.hr(), strings.hr())); - topPanel.add(ol = createPushButton(images.ol(), strings.ol())); - topPanel.add(ul = createPushButton(images.ul(), strings.ul())); - topPanel.add(insertImage = createPushButton(images.insertImage(), - strings.insertImage())); - topPanel.add(createLink = createPushButton(images.createLink(), - strings.createLink())); - topPanel.add(removeLink = createPushButton(images.removeLink(), - strings.removeLink())); - topPanel.add(removeFormat = createPushButton(images.removeFormat(), - strings.removeFormat())); - } - - if (basic != null) { - bottomPanel.add(backColors = createColorList("Background")); - bottomPanel.add(foreColors = createColorList("Foreground")); - bottomPanel.add(fonts = createFontList()); - bottomPanel.add(fontSizes = createFontSizes()); - - // We only use these listeners for updating status, so don't hook - // them up - // unless at least basic editing is supported. - richText.addKeyboardListener(listener); - richText.addClickListener(listener); - } - } - - private ListBox createColorList(String caption) { - final ListBox lb = new ListBox(); - lb.addChangeListener(listener); - lb.setVisibleItemCount(1); - - lb.addItem(caption); - lb.addItem(strings.white(), "white"); - lb.addItem(strings.black(), "black"); - lb.addItem(strings.red(), "red"); - lb.addItem(strings.green(), "green"); - lb.addItem(strings.yellow(), "yellow"); - lb.addItem(strings.blue(), "blue"); - return lb; - } - - private ListBox createFontList() { - final ListBox lb = new ListBox(); - lb.addChangeListener(listener); - lb.setVisibleItemCount(1); - - lb.addItem(strings.font(), ""); - lb.addItem(strings.normal(), ""); - lb.addItem("Times New Roman", "Times New Roman"); - lb.addItem("Arial", "Arial"); - lb.addItem("Courier New", "Courier New"); - lb.addItem("Georgia", "Georgia"); - lb.addItem("Trebuchet", "Trebuchet"); - lb.addItem("Verdana", "Verdana"); - return lb; - } - - private ListBox createFontSizes() { - final ListBox lb = new ListBox(); - lb.addChangeListener(listener); - lb.setVisibleItemCount(1); - - lb.addItem(strings.size()); - lb.addItem(strings.xxsmall()); - lb.addItem(strings.xsmall()); - lb.addItem(strings.small()); - lb.addItem(strings.medium()); - lb.addItem(strings.large()); - lb.addItem(strings.xlarge()); - lb.addItem(strings.xxlarge()); - return lb; - } - - private PushButton createPushButton(AbstractImagePrototype img, String tip) { - final PushButton pb = new PushButton(img.createImage()); - pb.addClickListener(listener); - pb.setTitle(tip); - return pb; - } - - private ToggleButton createToggleButton(AbstractImagePrototype img, - String tip) { - final ToggleButton tb = new ToggleButton(img.createImage()); - tb.addClickListener(listener); - tb.setTitle(tip); - return tb; - } - - /** - * Updates the status of all the stateful buttons. - */ - private void updateStatus() { - if (basic != null) { - bold.setDown(basic.isBold()); - italic.setDown(basic.isItalic()); - underline.setDown(basic.isUnderlined()); - subscript.setDown(basic.isSubscript()); - superscript.setDown(basic.isSuperscript()); - } - - if (extended != null) { - strikethrough.setDown(extended.isStrikethrough()); - } - } -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/richtextarea/VRichTextArea.java b/src/com/vaadin/terminal/gwt/client/ui/richtextarea/VRichTextArea.java new file mode 100644 index 0000000000..fea431edcd --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/richtextarea/VRichTextArea.java @@ -0,0 +1,251 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui.richtextarea; + +import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.DeferredCommand; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.ChangeListener; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.FocusListener; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.KeyboardListener; +import com.google.gwt.user.client.ui.RichTextArea; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.Paintable; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.Util; +import com.vaadin.terminal.gwt.client.ui.Field; + +/** + * This class implements a basic client side rich text editor component. + * + * @author IT Mill Ltd. + * + */ +public class VRichTextArea extends Composite implements Paintable, Field, + ChangeListener, FocusListener, KeyboardListener { + + /** + * The input node CSS classname. + */ + public static final String CLASSNAME = "i-richtextarea"; + + protected String id; + + protected ApplicationConnection client; + + private boolean immediate = false; + + private RichTextArea rta = new RichTextArea(); + + private VRichTextToolbar formatter = new VRichTextToolbar(rta); + + private HTML html = new HTML(); + + private final FlowPanel fp = new FlowPanel(); + + private boolean enabled = true; + + private int extraHorizontalPixels = -1; + private int extraVerticalPixels = -1; + + private int maxLength = -1; + + private int toolbarNaturalWidth = 500; + + public VRichTextArea() { + fp.add(formatter); + + rta.setWidth("100%"); + rta.addFocusListener(this); + + fp.add(rta); + + initWidget(fp); + setStyleName(CLASSNAME); + + } + + public void setEnabled(boolean enabled) { + if (this.enabled != enabled) { + rta.setEnabled(enabled); + if (enabled) { + fp.remove(html); + fp.add(rta); + } else { + html.setHTML(rta.getHTML()); + fp.remove(rta); + fp.add(html); + } + + this.enabled = enabled; + } + } + + public void updateFromUIDL(final UIDL uidl, ApplicationConnection client) { + this.client = client; + id = uidl.getId(); + + if (uidl.hasVariable("text")) { + if (BrowserInfo.get().isIE()) { + // rta is rather buggy in IE (as pretty much everything is) + // it needs some "shaking" not to fall into uneditable state + // see #2374 + rta.getBasicFormatter().toggleBold(); + rta.getBasicFormatter().toggleBold(); + } + rta.setHTML(uidl.getStringVariable("text")); + + } + setEnabled(!uidl.getBooleanAttribute("disabled")); + + if (client.updateComponent(this, uidl, true)) { + return; + } + + immediate = uidl.getBooleanAttribute("immediate"); + int newMaxLength = uidl.hasAttribute("maxLength") ? uidl + .getIntAttribute("maxLength") : -1; + if (newMaxLength >= 0) { + if (maxLength == -1) { + rta.addKeyboardListener(this); + } + maxLength = newMaxLength; + } else if (maxLength != -1) { + getElement().setAttribute("maxlength", ""); + maxLength = -1; + rta.removeKeyboardListener(this); + } + } + + public void onChange(Widget sender) { + if (client != null && id != null) { + client.updateVariable(id, "text", rta.getText(), immediate); + } + } + + public void onFocus(Widget sender) { + + } + + public void onLostFocus(Widget sender) { + final String html = rta.getHTML(); + client.updateVariable(id, "text", html, immediate); + + } + + /** + * @return space used by components paddings and borders + */ + private int getExtraHorizontalPixels() { + if (extraHorizontalPixels < 0) { + detectExtraSizes(); + } + return extraHorizontalPixels; + } + + /** + * @return space used by components paddings and borders + */ + private int getExtraVerticalPixels() { + if (extraVerticalPixels < 0) { + detectExtraSizes(); + } + return extraVerticalPixels; + } + + /** + * Detects space used by components paddings and borders. + */ + private void detectExtraSizes() { + Element clone = Util.cloneNode(getElement(), false); + DOM.setElementAttribute(clone, "id", ""); + DOM.setStyleAttribute(clone, "visibility", "hidden"); + DOM.setStyleAttribute(clone, "position", "absolute"); + // due FF3 bug set size to 10px and later subtract it from extra pixels + DOM.setStyleAttribute(clone, "width", "10px"); + DOM.setStyleAttribute(clone, "height", "10px"); + DOM.appendChild(DOM.getParent(getElement()), clone); + extraHorizontalPixels = DOM.getElementPropertyInt(clone, "offsetWidth") - 10; + extraVerticalPixels = DOM.getElementPropertyInt(clone, "offsetHeight") - 10; + + DOM.removeChild(DOM.getParent(getElement()), clone); + } + + @Override + public void setHeight(String height) { + if (height.endsWith("px")) { + int h = Integer.parseInt(height.substring(0, height.length() - 2)); + h -= getExtraVerticalPixels(); + if (h < 0) { + h = 0; + } + + super.setHeight(h + "px"); + } else { + super.setHeight(height); + } + + if (height == null || height.equals("")) { + rta.setHeight(""); + } else { + int editorHeight = getOffsetHeight() - getExtraVerticalPixels() + - formatter.getOffsetHeight(); + rta.setHeight(editorHeight + "px"); + } + } + + @Override + public void setWidth(String width) { + if (width.endsWith("px")) { + int w = Integer.parseInt(width.substring(0, width.length() - 2)); + w -= getExtraHorizontalPixels(); + if (w < 0) { + w = 0; + } + + super.setWidth(w + "px"); + } else if (width.equals("")) { + /* + * IE cannot calculate the width of the 100% iframe correctly if + * there is no width specified for the parent. In this case we would + * use the toolbar but IE cannot calculate the width of that one + * correctly either in all cases. So we end up using a default width + * for a RichTextArea with no width definition in all browsers (for + * compatibility). + */ + + super.setWidth(toolbarNaturalWidth + "px"); + } else { + super.setWidth(width); + } + } + + public void onKeyDown(Widget sender, char keyCode, int modifiers) { + // NOP + } + + public void onKeyPress(Widget sender, char keyCode, int modifiers) { + if (maxLength >= 0) { + DeferredCommand.addCommand(new Command() { + public void execute() { + if (rta.getHTML().length() > maxLength) { + rta.setHTML(rta.getHTML().substring(0, maxLength)); + } + } + }); + } + } + + public void onKeyUp(Widget sender, char keyCode, int modifiers) { + // NOP + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/richtextarea/VRichTextToolbar$Strings.properties b/src/com/vaadin/terminal/gwt/client/ui/richtextarea/VRichTextToolbar$Strings.properties new file mode 100644 index 0000000000..363b704584 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/richtextarea/VRichTextToolbar$Strings.properties @@ -0,0 +1,35 @@ +bold = Toggle Bold +createLink = Create Link +hr = Insert Horizontal Rule +indent = Indent Right +insertImage = Insert Image +italic = Toggle Italic +justifyCenter = Center +justifyLeft = Left Justify +justifyRight = Right Justify +ol = Insert Ordered List +outdent = Indent Left +removeFormat = Remove Formatting +removeLink = Remove Link +strikeThrough = Toggle Strikethrough +subscript = Toggle Subscript +superscript = Toggle Superscript +ul = Insert Unordered List +underline = Toggle Underline +color = Color +black = Black +white = White +red = Red +green = Green +yellow = Yellow +blue = Blue +font = Font +normal = Normal +size = Size +xxsmall = XX-Small +xsmall = X-Small +small = Small +medium = Medium +large = Large +xlarge = X-Large +xxlarge = XX-Large \ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/client/ui/richtextarea/VRichTextToolbar.java b/src/com/vaadin/terminal/gwt/client/ui/richtextarea/VRichTextToolbar.java new file mode 100644 index 0000000000..5325550b08 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/richtextarea/VRichTextToolbar.java @@ -0,0 +1,474 @@ +/* + * Copyright 2007 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.terminal.gwt.client.ui.richtextarea; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.i18n.client.Constants; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.ui.AbstractImagePrototype; +import com.google.gwt.user.client.ui.ChangeListener; +import com.google.gwt.user.client.ui.ClickListener; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.ImageBundle; +import com.google.gwt.user.client.ui.KeyboardListener; +import com.google.gwt.user.client.ui.ListBox; +import com.google.gwt.user.client.ui.PushButton; +import com.google.gwt.user.client.ui.RichTextArea; +import com.google.gwt.user.client.ui.ToggleButton; +import com.google.gwt.user.client.ui.Widget; + +/** + * A modified version of sample toolbar for use with {@link RichTextArea}. It + * provides a simple UI for all rich text formatting, dynamically displayed only + * for the available functionality. + */ +public class VRichTextToolbar extends Composite { + + /** + * This {@link ImageBundle} is used for all the button icons. Using an image + * bundle allows all of these images to be packed into a single image, which + * saves a lot of HTTP requests, drastically improving startup time. + */ + public interface Images extends ImageBundle { + + @ImageBundle.Resource("bold.gif") + AbstractImagePrototype bold(); + + @ImageBundle.Resource("createLink.gif") + AbstractImagePrototype createLink(); + + @ImageBundle.Resource("hr.gif") + AbstractImagePrototype hr(); + + @ImageBundle.Resource("indent.gif") + AbstractImagePrototype indent(); + + @ImageBundle.Resource("insertImage.gif") + AbstractImagePrototype insertImage(); + + @ImageBundle.Resource("italic.gif") + AbstractImagePrototype italic(); + + @ImageBundle.Resource("justifyCenter.gif") + AbstractImagePrototype justifyCenter(); + + @ImageBundle.Resource("justifyLeft.gif") + AbstractImagePrototype justifyLeft(); + + @ImageBundle.Resource("justifyRight.gif") + AbstractImagePrototype justifyRight(); + + @ImageBundle.Resource("ol.gif") + AbstractImagePrototype ol(); + + @ImageBundle.Resource("outdent.gif") + AbstractImagePrototype outdent(); + + @ImageBundle.Resource("removeFormat.gif") + AbstractImagePrototype removeFormat(); + + @ImageBundle.Resource("removeLink.gif") + AbstractImagePrototype removeLink(); + + @ImageBundle.Resource("strikeThrough.gif") + AbstractImagePrototype strikeThrough(); + + @ImageBundle.Resource("subscript.gif") + AbstractImagePrototype subscript(); + + @ImageBundle.Resource("superscript.gif") + AbstractImagePrototype superscript(); + + @ImageBundle.Resource("ul.gif") + AbstractImagePrototype ul(); + + @ImageBundle.Resource("underline.gif") + AbstractImagePrototype underline(); + } + + /** + * This {@link Constants} interface is used to make the toolbar's strings + * internationalizable. + */ + public interface Strings extends Constants { + + String black(); + + String blue(); + + String bold(); + + String color(); + + String createLink(); + + String font(); + + String green(); + + String hr(); + + String indent(); + + String insertImage(); + + String italic(); + + String justifyCenter(); + + String justifyLeft(); + + String justifyRight(); + + String large(); + + String medium(); + + String normal(); + + String ol(); + + String outdent(); + + String red(); + + String removeFormat(); + + String removeLink(); + + String size(); + + String small(); + + String strikeThrough(); + + String subscript(); + + String superscript(); + + String ul(); + + String underline(); + + String white(); + + String xlarge(); + + String xsmall(); + + String xxlarge(); + + String xxsmall(); + + String yellow(); + } + + /** + * We use an inner EventListener class to avoid exposing event methods on + * the RichTextToolbar itself. + */ + private class EventListener implements ClickListener, ChangeListener, + KeyboardListener { + + public void onChange(Widget sender) { + if (sender == backColors) { + basic.setBackColor(backColors.getValue(backColors + .getSelectedIndex())); + backColors.setSelectedIndex(0); + } else if (sender == foreColors) { + basic.setForeColor(foreColors.getValue(foreColors + .getSelectedIndex())); + foreColors.setSelectedIndex(0); + } else if (sender == fonts) { + basic.setFontName(fonts.getValue(fonts.getSelectedIndex())); + fonts.setSelectedIndex(0); + } else if (sender == fontSizes) { + basic.setFontSize(fontSizesConstants[fontSizes + .getSelectedIndex() - 1]); + fontSizes.setSelectedIndex(0); + } + } + + public void onClick(Widget sender) { + if (sender == bold) { + basic.toggleBold(); + } else if (sender == italic) { + basic.toggleItalic(); + } else if (sender == underline) { + basic.toggleUnderline(); + } else if (sender == subscript) { + basic.toggleSubscript(); + } else if (sender == superscript) { + basic.toggleSuperscript(); + } else if (sender == strikethrough) { + extended.toggleStrikethrough(); + } else if (sender == indent) { + extended.rightIndent(); + } else if (sender == outdent) { + extended.leftIndent(); + } else if (sender == justifyLeft) { + basic.setJustification(RichTextArea.Justification.LEFT); + } else if (sender == justifyCenter) { + basic.setJustification(RichTextArea.Justification.CENTER); + } else if (sender == justifyRight) { + basic.setJustification(RichTextArea.Justification.RIGHT); + } else if (sender == insertImage) { + final String url = Window.prompt("Enter an image URL:", + "http://"); + if (url != null) { + extended.insertImage(url); + } + } else if (sender == createLink) { + final String url = Window + .prompt("Enter a link URL:", "http://"); + if (url != null) { + extended.createLink(url); + } + } else if (sender == removeLink) { + extended.removeLink(); + } else if (sender == hr) { + extended.insertHorizontalRule(); + } else if (sender == ol) { + extended.insertOrderedList(); + } else if (sender == ul) { + extended.insertUnorderedList(); + } else if (sender == removeFormat) { + extended.removeFormat(); + } else if (sender == richText) { + // We use the RichTextArea's onKeyUp event to update the toolbar + // status. + // This will catch any cases where the user moves the cursur + // using the + // keyboard, or uses one of the browser's built-in keyboard + // shortcuts. + updateStatus(); + } + } + + public void onKeyDown(Widget sender, char keyCode, int modifiers) { + } + + public void onKeyPress(Widget sender, char keyCode, int modifiers) { + } + + public void onKeyUp(Widget sender, char keyCode, int modifiers) { + if (sender == richText) { + // We use the RichTextArea's onKeyUp event to update the toolbar + // status. + // This will catch any cases where the user moves the cursur + // using the + // keyboard, or uses one of the browser's built-in keyboard + // shortcuts. + updateStatus(); + } + } + } + + private static final RichTextArea.FontSize[] fontSizesConstants = new RichTextArea.FontSize[] { + RichTextArea.FontSize.XX_SMALL, RichTextArea.FontSize.X_SMALL, + RichTextArea.FontSize.SMALL, RichTextArea.FontSize.MEDIUM, + RichTextArea.FontSize.LARGE, RichTextArea.FontSize.X_LARGE, + RichTextArea.FontSize.XX_LARGE }; + + private final Images images = (Images) GWT.create(Images.class); + private final Strings strings = (Strings) GWT.create(Strings.class); + private final EventListener listener = new EventListener(); + + private final RichTextArea richText; + private final RichTextArea.BasicFormatter basic; + private final RichTextArea.ExtendedFormatter extended; + + private final FlowPanel outer = new FlowPanel(); + private final FlowPanel topPanel = new FlowPanel(); + private final FlowPanel bottomPanel = new FlowPanel(); + private ToggleButton bold; + private ToggleButton italic; + private ToggleButton underline; + private ToggleButton subscript; + private ToggleButton superscript; + private ToggleButton strikethrough; + private PushButton indent; + private PushButton outdent; + private PushButton justifyLeft; + private PushButton justifyCenter; + private PushButton justifyRight; + private PushButton hr; + private PushButton ol; + private PushButton ul; + private PushButton insertImage; + private PushButton createLink; + private PushButton removeLink; + private PushButton removeFormat; + + private ListBox backColors; + private ListBox foreColors; + private ListBox fonts; + private ListBox fontSizes; + + /** + * Creates a new toolbar that drives the given rich text area. + * + * @param richText + * the rich text area to be controlled + */ + public VRichTextToolbar(RichTextArea richText) { + this.richText = richText; + basic = richText.getBasicFormatter(); + extended = richText.getExtendedFormatter(); + + outer.add(topPanel); + outer.add(bottomPanel); + topPanel.setWidth("100%"); + topPanel.setHeight("20px"); + topPanel.getElement().getStyle().setProperty("overflow", "hidden"); + bottomPanel.setWidth("100%"); + + initWidget(outer); + setStyleName("gwt-RichTextToolbar"); + + if (basic != null) { + topPanel.add(bold = createToggleButton(images.bold(), strings + .bold())); + topPanel.add(italic = createToggleButton(images.italic(), strings + .italic())); + topPanel.add(underline = createToggleButton(images.underline(), + strings.underline())); + topPanel.add(subscript = createToggleButton(images.subscript(), + strings.subscript())); + topPanel.add(superscript = createToggleButton(images.superscript(), + strings.superscript())); + topPanel.add(justifyLeft = createPushButton(images.justifyLeft(), + strings.justifyLeft())); + topPanel.add(justifyCenter = createPushButton(images + .justifyCenter(), strings.justifyCenter())); + topPanel.add(justifyRight = createPushButton(images.justifyRight(), + strings.justifyRight())); + } + + if (extended != null) { + topPanel.add(strikethrough = createToggleButton(images + .strikeThrough(), strings.strikeThrough())); + topPanel.add(indent = createPushButton(images.indent(), strings + .indent())); + topPanel.add(outdent = createPushButton(images.outdent(), strings + .outdent())); + topPanel.add(hr = createPushButton(images.hr(), strings.hr())); + topPanel.add(ol = createPushButton(images.ol(), strings.ol())); + topPanel.add(ul = createPushButton(images.ul(), strings.ul())); + topPanel.add(insertImage = createPushButton(images.insertImage(), + strings.insertImage())); + topPanel.add(createLink = createPushButton(images.createLink(), + strings.createLink())); + topPanel.add(removeLink = createPushButton(images.removeLink(), + strings.removeLink())); + topPanel.add(removeFormat = createPushButton(images.removeFormat(), + strings.removeFormat())); + } + + if (basic != null) { + bottomPanel.add(backColors = createColorList("Background")); + bottomPanel.add(foreColors = createColorList("Foreground")); + bottomPanel.add(fonts = createFontList()); + bottomPanel.add(fontSizes = createFontSizes()); + + // We only use these listeners for updating status, so don't hook + // them up + // unless at least basic editing is supported. + richText.addKeyboardListener(listener); + richText.addClickListener(listener); + } + } + + private ListBox createColorList(String caption) { + final ListBox lb = new ListBox(); + lb.addChangeListener(listener); + lb.setVisibleItemCount(1); + + lb.addItem(caption); + lb.addItem(strings.white(), "white"); + lb.addItem(strings.black(), "black"); + lb.addItem(strings.red(), "red"); + lb.addItem(strings.green(), "green"); + lb.addItem(strings.yellow(), "yellow"); + lb.addItem(strings.blue(), "blue"); + return lb; + } + + private ListBox createFontList() { + final ListBox lb = new ListBox(); + lb.addChangeListener(listener); + lb.setVisibleItemCount(1); + + lb.addItem(strings.font(), ""); + lb.addItem(strings.normal(), ""); + lb.addItem("Times New Roman", "Times New Roman"); + lb.addItem("Arial", "Arial"); + lb.addItem("Courier New", "Courier New"); + lb.addItem("Georgia", "Georgia"); + lb.addItem("Trebuchet", "Trebuchet"); + lb.addItem("Verdana", "Verdana"); + return lb; + } + + private ListBox createFontSizes() { + final ListBox lb = new ListBox(); + lb.addChangeListener(listener); + lb.setVisibleItemCount(1); + + lb.addItem(strings.size()); + lb.addItem(strings.xxsmall()); + lb.addItem(strings.xsmall()); + lb.addItem(strings.small()); + lb.addItem(strings.medium()); + lb.addItem(strings.large()); + lb.addItem(strings.xlarge()); + lb.addItem(strings.xxlarge()); + return lb; + } + + private PushButton createPushButton(AbstractImagePrototype img, String tip) { + final PushButton pb = new PushButton(img.createImage()); + pb.addClickListener(listener); + pb.setTitle(tip); + return pb; + } + + private ToggleButton createToggleButton(AbstractImagePrototype img, + String tip) { + final ToggleButton tb = new ToggleButton(img.createImage()); + tb.addClickListener(listener); + tb.setTitle(tip); + return tb; + } + + /** + * Updates the status of all the stateful buttons. + */ + private void updateStatus() { + if (basic != null) { + bold.setDown(basic.isBold()); + italic.setDown(basic.isItalic()); + underline.setDown(basic.isUnderlined()); + subscript.setDown(basic.isSubscript()); + superscript.setDown(basic.isSuperscript()); + } + + if (extended != null) { + strikethrough.setDown(extended.isStrikethrough()); + } + } +} diff --git a/src/com/vaadin/ui/AbsoluteLayout.java b/src/com/vaadin/ui/AbsoluteLayout.java index cf3f62eee4..0852d7bc50 100644 --- a/src/com/vaadin/ui/AbsoluteLayout.java +++ b/src/com/vaadin/ui/AbsoluteLayout.java @@ -9,7 +9,7 @@ import java.util.Map; import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; -import com.vaadin.terminal.gwt.client.ui.IAbsoluteLayout; +import com.vaadin.terminal.gwt.client.ui.VAbsoluteLayout; /** * AbsoluteLayout is a layout implementation that mimics html absolute @@ -28,7 +28,7 @@ public class AbsoluteLayout extends AbstractLayout { @Override public String getTag() { - return IAbsoluteLayout.TAGNAME; + return VAbsoluteLayout.TAGNAME; } public Iterator getComponentIterator() { diff --git a/src/com/vaadin/ui/DateField.java b/src/com/vaadin/ui/DateField.java index fc88187c06..d782efbb13 100644 --- a/src/com/vaadin/ui/DateField.java +++ b/src/com/vaadin/ui/DateField.java @@ -109,7 +109,7 @@ public class DateField extends AbstractField { private String dateFormat; /** - * Read-only content of an ITextualDate field - null for other types of date + * Read-only content of an VTextualDate field - null for other types of date * fields. */ private String dateString; diff --git a/src/com/vaadin/ui/Layout.java b/src/com/vaadin/ui/Layout.java index 935ae363b5..f735b86023 100644 --- a/src/com/vaadin/ui/Layout.java +++ b/src/com/vaadin/ui/Layout.java @@ -6,7 +6,7 @@ package com.vaadin.ui; import java.io.Serializable; -import com.vaadin.terminal.gwt.client.ui.IMarginInfo; +import com.vaadin.terminal.gwt.client.ui.VMarginInfo; import com.vaadin.terminal.gwt.client.ui.AlignmentInfo.Bits; /** @@ -224,7 +224,7 @@ public interface Layout extends ComponentContainer, Serializable { } @SuppressWarnings("serial") - public static class MarginInfo extends IMarginInfo implements Serializable { + public static class MarginInfo extends VMarginInfo implements Serializable { public MarginInfo(boolean enabled) { super(enabled, enabled, enabled, enabled);