123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660 |
- /*
- @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.event.dom.client.ClickEvent;
- import com.google.gwt.event.dom.client.ClickHandler;
- 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.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;
- import com.vaadin.terminal.gwt.client.VCaption;
-
- public class VAccordion extends VTabsheetBase implements
- ContainerResizedListener {
-
- public static final String CLASSNAME = "v-accordion";
-
- private Set<Paintable> paintables = new HashSet<Paintable>();
-
- private String height;
-
- private String width = "";
-
- private HashMap<StackItem, UIDL> lazyUpdateMap = new HashMap<StackItem, UIDL>();
-
- 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;
- }
-
- Util.setWidthExcludingPaddingAndBorder(this, width, 2);
- 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) {
- Util.setHeightExcludingPaddingAndBorder(this, height, 2);
- 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 ClickHandler {
-
- 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.addClickHandler(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);
- setWidth("");
- if (BrowserInfo.get().isIE6()) {
- // Work around for IE6 layouting problem #3359
- getElement().getStyle().setProperty("zoom", "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(ClickEvent event) {
- 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<Paintable> 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
- */
- // layout size change may affect its available space (scrollbars)
- for (Paintable paintable : child) {
- client.handleComponentRelativeSize((Widget) paintable);
- }
-
- 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);
- }
- }
|