You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

IExpandLayout.java 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.itmill.toolkit.terminal.gwt.client.ui;
  5. import java.util.ArrayList;
  6. import java.util.HashMap;
  7. import java.util.Iterator;
  8. import java.util.Set;
  9. import com.google.gwt.user.client.Command;
  10. import com.google.gwt.user.client.DOM;
  11. import com.google.gwt.user.client.DeferredCommand;
  12. import com.google.gwt.user.client.Element;
  13. import com.google.gwt.user.client.ui.ComplexPanel;
  14. import com.google.gwt.user.client.ui.RootPanel;
  15. import com.google.gwt.user.client.ui.UIObject;
  16. import com.google.gwt.user.client.ui.Widget;
  17. import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
  18. import com.itmill.toolkit.terminal.gwt.client.BrowserInfo;
  19. import com.itmill.toolkit.terminal.gwt.client.Container;
  20. import com.itmill.toolkit.terminal.gwt.client.ContainerResizedListener;
  21. import com.itmill.toolkit.terminal.gwt.client.ICaption;
  22. import com.itmill.toolkit.terminal.gwt.client.Paintable;
  23. import com.itmill.toolkit.terminal.gwt.client.RenderInformation;
  24. import com.itmill.toolkit.terminal.gwt.client.RenderSpace;
  25. import com.itmill.toolkit.terminal.gwt.client.StyleConstants;
  26. import com.itmill.toolkit.terminal.gwt.client.UIDL;
  27. /**
  28. * @author IT Mill Ltd
  29. */
  30. public class IExpandLayout extends ComplexPanel implements
  31. ContainerResizedListener, Container {
  32. public static final String CLASSNAME = "i-expandlayout";
  33. public static final int ORIENTATION_HORIZONTAL = 1;
  34. public static final int ORIENTATION_VERTICAL = 0;
  35. /**
  36. * Minimum pixels reserved for expanded element to avoid "odd" situations
  37. * where expanded element is 0 size. Default is 5 pixels to show user a hint
  38. * that there is a component. Then user can often use splitpanel or resize
  39. * window to show component properly. This value may be insane in some
  40. * applications. Override this to specify a proper for your case.
  41. */
  42. protected static final int EXPANDED_ELEMENTS_MIN_WIDTH = 5;
  43. /**
  44. * Contains reference to Element where Paintables are wrapped.
  45. */
  46. protected Element childContainer;
  47. protected ApplicationConnection client = null;
  48. protected HashMap<Paintable, ICaption> componentToCaption = new HashMap<Paintable, ICaption>();
  49. /*
  50. * Elements that provides the Layout interface implementation.
  51. */
  52. protected Element element;
  53. private Widget expandedWidget = null;
  54. private UIDL expandedWidgetUidl;
  55. int orientationMode = ORIENTATION_VERTICAL;
  56. protected int topMargin = -1;
  57. private String width;
  58. private String height;
  59. private Element marginElement;
  60. private Element breakElement;
  61. private int bottomMargin = -1;
  62. private boolean hasComponentSpacing;
  63. private int spacingSize = -1;
  64. private boolean rendering;
  65. private RenderInformation renderInformation = new RenderInformation();
  66. private int spaceForExpandedWidget;
  67. public IExpandLayout() {
  68. this(IExpandLayout.ORIENTATION_VERTICAL);
  69. }
  70. public IExpandLayout(int orientation) {
  71. orientationMode = orientation;
  72. constructDOM();
  73. setStyleName(CLASSNAME);
  74. }
  75. public void add(Widget w) {
  76. final WidgetWrapper wrapper = createWidgetWrappper();
  77. DOM.appendChild(childContainer, wrapper.getElement());
  78. super.add(w, wrapper.getContainerElement());
  79. }
  80. protected void constructDOM() {
  81. element = DOM.createDiv();
  82. // DOM.setStyleAttribute(element, "overflow", "hidden");
  83. if (orientationMode == ORIENTATION_HORIZONTAL) {
  84. marginElement = DOM.createDiv();
  85. if (BrowserInfo.get().isIE()) {
  86. DOM.setStyleAttribute(marginElement, "zoom", "1");
  87. DOM.setStyleAttribute(marginElement, "overflow", "hidden");
  88. }
  89. childContainer = DOM.createDiv();
  90. if (BrowserInfo.get().isIE()) {
  91. DOM.setStyleAttribute(childContainer, "zoom", "1");
  92. DOM.setStyleAttribute(childContainer, "overflow", "hidden");
  93. }
  94. DOM.setStyleAttribute(childContainer, "height", "100%");
  95. breakElement = DOM.createDiv();
  96. DOM.setStyleAttribute(breakElement, "overflow", "hidden");
  97. DOM.setStyleAttribute(breakElement, "height", "0px");
  98. DOM.setStyleAttribute(breakElement, "clear", "both");
  99. DOM.appendChild(marginElement, childContainer);
  100. DOM.appendChild(marginElement, breakElement);
  101. DOM.appendChild(element, marginElement);
  102. } else {
  103. childContainer = DOM.createDiv();
  104. DOM.appendChild(element, childContainer);
  105. marginElement = childContainer;
  106. }
  107. setElement(element);
  108. }
  109. protected WidgetWrapper createWidgetWrappper() {
  110. if (orientationMode == ORIENTATION_HORIZONTAL) {
  111. return new HorizontalWidgetWrapper();
  112. } else {
  113. return new VerticalWidgetWrapper();
  114. }
  115. }
  116. /**
  117. * Returns given widgets WidgetWrapper
  118. *
  119. * @param child
  120. * @return
  121. */
  122. public WidgetWrapper getWidgetWrapperFor(Widget child) {
  123. final Element containerElement = DOM.getParent(child.getElement());
  124. if (orientationMode == ORIENTATION_HORIZONTAL) {
  125. return new HorizontalWidgetWrapper(containerElement);
  126. } else {
  127. return new VerticalWidgetWrapper(DOM.getParent(containerElement));
  128. }
  129. }
  130. abstract class WidgetWrapper extends UIObject {
  131. /**
  132. * @return element that contains Widget
  133. */
  134. public Element getContainerElement() {
  135. return getElement();
  136. }
  137. public Element getCaptionContainer() {
  138. return getElement();
  139. }
  140. abstract void setExpandedSize(int pixels);
  141. abstract void setAlignment(String verticalAlignment,
  142. String horizontalAlignment);
  143. abstract void setSpacingEnabled(boolean b);
  144. }
  145. class VerticalWidgetWrapper extends WidgetWrapper {
  146. public VerticalWidgetWrapper(Element div) {
  147. setElement(div);
  148. }
  149. public VerticalWidgetWrapper() {
  150. setElement(DOM.createDiv());
  151. DOM.appendChild(getElement(), DOM.createDiv());
  152. DOM.setStyleAttribute(getElement(), "overflow", "hidden");
  153. // Set to 'hidden' at first (prevent IE6 content overflows), and set
  154. // to 'auto' later.
  155. DOM.setStyleAttribute(getContainerElement(), "overflow", "hidden");
  156. }
  157. public void setExpandedSize(int pixels) {
  158. Element firstChild = DOM.getFirstChild(getElement());
  159. int captionHeight = 0;
  160. if (firstChild != getContainerElement()) {
  161. captionHeight = firstChild.getOffsetHeight();
  162. }
  163. int fixedInnerSize = pixels - captionHeight;
  164. if (fixedInnerSize < 0) {
  165. fixedInnerSize = 0;
  166. }
  167. DOM.setStyleAttribute(getContainerElement(), "height",
  168. fixedInnerSize + "px");
  169. }
  170. void setAlignment(String verticalAlignment, String horizontalAlignment) {
  171. DOM.setStyleAttribute(getElement(), "textAlign",
  172. horizontalAlignment);
  173. // ignoring vertical alignment
  174. }
  175. void setSpacingEnabled(boolean b) {
  176. setStyleName(getElement(), CLASSNAME + "-"
  177. + StyleConstants.VERTICAL_SPACING, b);
  178. }
  179. public Element getContainerElement() {
  180. return getElement().getLastChild().cast();
  181. }
  182. public Element getCaptionElement() {
  183. return getElement();
  184. }
  185. }
  186. class HorizontalWidgetWrapper extends WidgetWrapper {
  187. private Element td;
  188. private String valign = "top";
  189. private String align = "left";
  190. public HorizontalWidgetWrapper(Element element) {
  191. if (DOM.getElementProperty(element, "nodeName").equals("TD")) {
  192. td = element;
  193. setElement(DOM.getParent(DOM.getParent(DOM.getParent(DOM
  194. .getParent(td)))));
  195. } else {
  196. setElement(element);
  197. }
  198. }
  199. public HorizontalWidgetWrapper() {
  200. setElement(DOM.createDiv());
  201. DOM.setStyleAttribute(getElement(), "cssFloat", "left");
  202. if (BrowserInfo.get().isIE()) {
  203. DOM.setStyleAttribute(getElement(), "styleFloat", "left");
  204. }
  205. DOM.setStyleAttribute(getElement(), "height", "100%");
  206. }
  207. public void setExpandedSize(int pixels) {
  208. setWidth(pixels + "px");
  209. DOM.setStyleAttribute(getElement(), "overflow", "hidden");
  210. }
  211. void setAlignment(String verticalAlignment, String horizontalAlignment) {
  212. DOM.setStyleAttribute(getElement(), "verticalAlign",
  213. verticalAlignment);
  214. if (!valign.equals(verticalAlignment)) {
  215. if (verticalAlignment.equals("top")) {
  216. // remove table, move content to div
  217. } else {
  218. if (td == null) {
  219. // build one cell table
  220. final Element table = DOM.createTable();
  221. final Element tBody = DOM.createTBody();
  222. final Element tr = DOM.createTR();
  223. td = DOM.createTD();
  224. DOM.appendChild(table, tBody);
  225. DOM.appendChild(tBody, tr);
  226. DOM.appendChild(tr, td);
  227. DOM.setElementProperty(table, "className", CLASSNAME
  228. + "-valign");
  229. DOM.setElementProperty(tr, "className", CLASSNAME
  230. + "-valign");
  231. DOM.setElementProperty(td, "className", CLASSNAME
  232. + "-valign");
  233. // move possible content to cell
  234. final Element content = DOM.getFirstChild(getElement());
  235. if (content != null) {
  236. DOM.removeChild(getElement(), content);
  237. DOM.appendChild(td, content);
  238. }
  239. DOM.appendChild(getElement(), table);
  240. }
  241. // set alignment
  242. DOM.setStyleAttribute(td, "verticalAlign",
  243. verticalAlignment);
  244. }
  245. valign = verticalAlignment;
  246. }
  247. if (!align.equals(horizontalAlignment)) {
  248. DOM.setStyleAttribute(getContainerElement(), "textAlign",
  249. horizontalAlignment);
  250. align = horizontalAlignment;
  251. }
  252. }
  253. public Element getContainerElement() {
  254. if (td == null) {
  255. return super.getContainerElement();
  256. } else {
  257. return td;
  258. }
  259. }
  260. void setSpacingEnabled(boolean b) {
  261. setStyleName(getElement(), CLASSNAME + "-"
  262. + StyleConstants.HORIZONTAL_SPACING, b);
  263. }
  264. }
  265. protected ArrayList getPaintables() {
  266. final ArrayList al = new ArrayList();
  267. final Iterator it = iterator();
  268. while (it.hasNext()) {
  269. final Widget w = (Widget) it.next();
  270. if (w instanceof Paintable) {
  271. al.add(w);
  272. }
  273. }
  274. return al;
  275. }
  276. public Widget getWidget(int index) {
  277. return getChildren().get(index);
  278. }
  279. public int getWidgetCount() {
  280. return getChildren().size();
  281. }
  282. public int getWidgetIndex(Widget child) {
  283. return getChildren().indexOf(child);
  284. }
  285. protected void handleAlignments(UIDL uidl) {
  286. // Component alignments as a comma separated list.
  287. // See com.itmill.toolkit.terminal.gwt.client.ui.AlignmentInfo.java for
  288. // possible values.
  289. final int[] alignments = uidl.getIntArrayAttribute("alignments");
  290. int alignmentIndex = 0;
  291. // Set alignment attributes
  292. final Iterator it = getPaintables().iterator();
  293. boolean first = true;
  294. while (it.hasNext()) {
  295. // Calculate alignment info
  296. final AlignmentInfo ai = new AlignmentInfo(
  297. alignments[alignmentIndex++]);
  298. final WidgetWrapper wr = getWidgetWrapperFor((Widget) it.next());
  299. wr.setAlignment(ai.getVerticalAlignment(), ai
  300. .getHorizontalAlignment());
  301. if (first) {
  302. wr.setSpacingEnabled(false);
  303. first = false;
  304. } else {
  305. wr.setSpacingEnabled(hasComponentSpacing);
  306. }
  307. }
  308. }
  309. protected void handleMargins(UIDL uidl) {
  310. if (uidl.hasAttribute("margins")) {
  311. final MarginInfo margins = new MarginInfo(uidl
  312. .getIntAttribute("margins"));
  313. setStyleName(marginElement, CLASSNAME + "-"
  314. + StyleConstants.MARGIN_TOP, margins.hasTop());
  315. setStyleName(marginElement, CLASSNAME + "-"
  316. + StyleConstants.MARGIN_RIGHT, margins.hasRight());
  317. setStyleName(marginElement, CLASSNAME + "-"
  318. + StyleConstants.MARGIN_BOTTOM, margins.hasBottom());
  319. setStyleName(marginElement, CLASSNAME + "-"
  320. + StyleConstants.MARGIN_LEFT, margins.hasLeft());
  321. }
  322. }
  323. public boolean hasChildComponent(Widget component) {
  324. return getWidgetIndex(component) >= 0;
  325. }
  326. public void iLayout() {
  327. renderInformation.updateSize(getElement());
  328. if (orientationMode == ORIENTATION_HORIZONTAL) {
  329. int pixels;
  330. if ("".equals(height)) {
  331. // try to find minimum height by looping all widgets
  332. int maxHeight = 0;
  333. Iterator iterator = getPaintables().iterator();
  334. while (iterator.hasNext()) {
  335. Widget w = (Widget) iterator.next();
  336. int h = w.getOffsetHeight();
  337. if (h > maxHeight) {
  338. maxHeight = h;
  339. }
  340. }
  341. pixels = maxHeight;
  342. } else {
  343. pixels = getOffsetHeight() - getTopMargin() - getBottomMargin();
  344. if (pixels < 0) {
  345. pixels = 0;
  346. }
  347. }
  348. DOM.setStyleAttribute(marginElement, "height", pixels + "px");
  349. DOM.setStyleAttribute(marginElement, "overflow", "hidden");
  350. }
  351. if (expandedWidget == null) {
  352. return;
  353. }
  354. final int availableSpace = getAvailableSpace();
  355. // Cannot use root element for layout as it contains margins
  356. Element expandElement = expandedWidget.getElement();
  357. Element expandedParentElement = DOM.getParent(expandElement);
  358. if (orientationMode == ORIENTATION_VERTICAL) {
  359. renderInformation.setContentAreaWidth(expandedParentElement
  360. .getOffsetWidth());
  361. } else {
  362. renderInformation.setContentAreaHeight(expandedParentElement
  363. .getOffsetHeight());
  364. }
  365. final int usedSpace = getUsedSpace();
  366. spaceForExpandedWidget = availableSpace - usedSpace;
  367. if (spaceForExpandedWidget < EXPANDED_ELEMENTS_MIN_WIDTH) {
  368. // TODO fire warning for developer
  369. spaceForExpandedWidget = EXPANDED_ELEMENTS_MIN_WIDTH;
  370. }
  371. final WidgetWrapper wr = getWidgetWrapperFor(expandedWidget);
  372. wr.setExpandedSize(spaceForExpandedWidget);
  373. /*
  374. * Workaround for issue #2093. Gecko base brosers have some rounding
  375. * issues every now and then. If all elements didn't fit on same row,
  376. * decrease expanded space until they do.
  377. */
  378. if (orientationMode == ORIENTATION_HORIZONTAL
  379. && BrowserInfo.get().isGecko()) {
  380. int tries = 0;
  381. while (tries < 30
  382. && spaceForExpandedWidget > EXPANDED_ELEMENTS_MIN_WIDTH
  383. && isLastElementDropped()) {
  384. spaceForExpandedWidget--;
  385. wr.setExpandedSize(spaceForExpandedWidget);
  386. }
  387. }
  388. // setting overflow auto lazy off during layout function
  389. DOM.setStyleAttribute(expandedParentElement, "overflow", "hidden");
  390. // TODO save previous size and only propagate if really changed
  391. if (client != null) {
  392. client.runDescendentsLayout(this);
  393. }
  394. // setting overflow back to auto
  395. DOM.setStyleAttribute(expandedParentElement, "overflow", "auto");
  396. }
  397. /**
  398. * Helper method to build workaround for Gecko issue.
  399. *
  400. * @return true if last element has dropped to another line
  401. */
  402. private boolean isLastElementDropped() {
  403. int firstTop = DOM.getAbsoluteTop(DOM.getFirstChild(childContainer));
  404. int lastTop = DOM.getAbsoluteTop(DOM.getChild(childContainer, (DOM
  405. .getChildCount(childContainer) - 1)));
  406. return firstTop != lastTop;
  407. }
  408. private int getTopMargin() {
  409. if (topMargin < 0) {
  410. topMargin = DOM.getElementPropertyInt(childContainer, "offsetTop")
  411. - DOM.getElementPropertyInt(getElement(), "offsetTop");
  412. }
  413. if (topMargin < 0) {
  414. // FIXME shouldn't happen
  415. return 0;
  416. } else {
  417. return topMargin;
  418. }
  419. }
  420. private int getBottomMargin() {
  421. if (bottomMargin < 0) {
  422. bottomMargin = DOM
  423. .getElementPropertyInt(marginElement, "offsetTop")
  424. + DOM.getElementPropertyInt(marginElement, "offsetHeight")
  425. - DOM.getElementPropertyInt(breakElement, "offsetTop");
  426. if (bottomMargin < 0) {
  427. // FIXME shouldn't happen
  428. return 0;
  429. }
  430. }
  431. return bottomMargin;
  432. }
  433. private int getUsedSpace() {
  434. int total = 0;
  435. final int widgetCount = getWidgetCount();
  436. final Iterator it = iterator();
  437. while (it.hasNext()) {
  438. final Widget w = (Widget) it.next();
  439. if (w instanceof Paintable && w != expandedWidget) {
  440. final WidgetWrapper wr = getWidgetWrapperFor(w);
  441. if (orientationMode == ORIENTATION_VERTICAL) {
  442. total += wr.getOffsetHeight();
  443. } else {
  444. total += wr.getOffsetWidth();
  445. }
  446. }
  447. }
  448. total += getSpacingSize() * (widgetCount - 1);
  449. return total;
  450. }
  451. private int getSpacingSize() {
  452. if (hasComponentSpacing) {
  453. if (spacingSize < 0) {
  454. final Element temp = DOM.createDiv();
  455. final WidgetWrapper wr = createWidgetWrappper();
  456. wr.setSpacingEnabled(true);
  457. DOM.appendChild(temp, wr.getElement());
  458. DOM.setStyleAttribute(temp, "position", "absolute");
  459. DOM.setStyleAttribute(temp, "top", "0");
  460. DOM.setStyleAttribute(temp, "visibility", "hidden");
  461. DOM.appendChild(RootPanel.getBodyElement(), temp);
  462. if (orientationMode == ORIENTATION_HORIZONTAL) {
  463. spacingSize = DOM.getElementPropertyInt(wr.getElement(),
  464. "offsetLeft");
  465. } else {
  466. spacingSize = DOM.getElementPropertyInt(wr.getElement(),
  467. "offsetTop");
  468. }
  469. DOM.removeChild(RootPanel.getBodyElement(), temp);
  470. }
  471. return spacingSize;
  472. } else {
  473. return 0;
  474. }
  475. }
  476. private int getAvailableSpace() {
  477. int size;
  478. if (orientationMode == ORIENTATION_VERTICAL) {
  479. if (BrowserInfo.get().isIE6()) {
  480. DOM.setStyleAttribute(getElement(), "overflow", "hidden");
  481. }
  482. size = getOffsetHeight();
  483. if (BrowserInfo.get().isIE6()) {
  484. DOM.setStyleAttribute(getElement(), "overflow", "visible");
  485. }
  486. final int marginTop = DOM.getElementPropertyInt(DOM
  487. .getFirstChild(marginElement), "offsetTop")
  488. - DOM.getElementPropertyInt(element, "offsetTop");
  489. final Element lastElement = DOM.getChild(marginElement, (DOM
  490. .getChildCount(marginElement) - 1));
  491. final int marginBottom = DOM.getElementPropertyInt(marginElement,
  492. "offsetHeight")
  493. + DOM.getElementPropertyInt(marginElement, "offsetTop")
  494. - (DOM.getElementPropertyInt(lastElement, "offsetTop") + DOM
  495. .getElementPropertyInt(lastElement, "offsetHeight"));
  496. size -= (marginTop + marginBottom);
  497. } else {
  498. // horizontal mode
  499. size = DOM.getElementPropertyInt(breakElement, "offsetWidth");
  500. }
  501. return size;
  502. }
  503. protected void insert(Widget w, int beforeIndex) {
  504. if (w instanceof ICaption) {
  505. final ICaption c = (ICaption) w;
  506. WidgetWrapper wrapper = getWidgetWrapperFor((Widget) c.getOwner());
  507. Element captionContainer = wrapper.getCaptionContainer();
  508. final Element captionElement = DOM.createDiv();
  509. DOM.insertChild(captionContainer, captionElement, 0);
  510. insert(w, captionElement, beforeIndex, false);
  511. } else {
  512. final WidgetWrapper wrapper = createWidgetWrappper();
  513. DOM.insertChild(childContainer, wrapper.getElement(), beforeIndex);
  514. insert(w, wrapper.getContainerElement(), beforeIndex, false);
  515. }
  516. }
  517. public boolean remove(int index) {
  518. return remove(getWidget(index));
  519. }
  520. public boolean remove(Widget w) {
  521. final WidgetWrapper ww = getWidgetWrapperFor(w);
  522. final boolean removed = super.remove(w);
  523. if (removed) {
  524. if (!(w instanceof ICaption)) {
  525. DOM.removeChild(childContainer, ww.getElement());
  526. }
  527. return true;
  528. }
  529. return false;
  530. }
  531. public void removeCaption(Widget w) {
  532. final ICaption c = componentToCaption.get(w);
  533. if (c != null) {
  534. this.remove(c);
  535. componentToCaption.remove(w);
  536. }
  537. }
  538. public boolean removePaintable(Paintable p) {
  539. final ICaption c = componentToCaption.get(p);
  540. if (c != null) {
  541. componentToCaption.remove(c);
  542. remove(c);
  543. }
  544. client.unregisterPaintable(p);
  545. if (expandedWidget == p) {
  546. expandedWidget = null;
  547. }
  548. return remove((Widget) p);
  549. }
  550. public void replaceChildComponent(Widget from, Widget to) {
  551. client.unregisterPaintable((Paintable) from);
  552. final ICaption c = componentToCaption.get(from);
  553. if (c != null) {
  554. remove(c);
  555. componentToCaption.remove(c);
  556. }
  557. final int index = getWidgetIndex(from);
  558. if (index >= 0) {
  559. remove(index);
  560. insert(to, index);
  561. }
  562. }
  563. public void updateCaption(Paintable component, UIDL uidl) {
  564. ICaption c = componentToCaption.get(component);
  565. boolean captionSizeMayHaveChanged = false;
  566. if (ICaption.isNeeded(uidl)) {
  567. if (c == null) {
  568. final int index = getWidgetIndex((Widget) component);
  569. c = new ICaption(component, client);
  570. insert(c, index);
  571. componentToCaption.put(component, c);
  572. captionSizeMayHaveChanged = true;
  573. }
  574. c.updateCaption(uidl);
  575. } else {
  576. if (c != null) {
  577. remove(c);
  578. componentToCaption.remove(component);
  579. captionSizeMayHaveChanged = true;
  580. }
  581. }
  582. if (!rendering && captionSizeMayHaveChanged) {
  583. iLayout();
  584. }
  585. }
  586. public void setWidth(String newWidth) {
  587. if (newWidth.equals(width)) {
  588. return;
  589. }
  590. width = newWidth;
  591. super.setWidth(width);
  592. }
  593. public void setHeight(String newHeight) {
  594. if (newHeight.equals(height)) {
  595. return;
  596. }
  597. height = newHeight;
  598. super.setHeight(height);
  599. if (orientationMode == ORIENTATION_HORIZONTAL) {
  600. iLayout();
  601. }
  602. }
  603. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  604. rendering = true;
  605. this.client = client;
  606. // Modify layout margins
  607. handleMargins(uidl);
  608. if (client.updateComponent(this, uidl, true)) {
  609. rendering = false;
  610. return;
  611. }
  612. hasComponentSpacing = uidl.getBooleanAttribute("spacing");
  613. final ArrayList uidlWidgets = new ArrayList();
  614. for (final Iterator it = uidl.getChildIterator(); it.hasNext();) {
  615. final UIDL cellUidl = (UIDL) it.next();
  616. final Paintable child = client.getPaintable(cellUidl
  617. .getChildUIDL(0));
  618. uidlWidgets.add(child);
  619. if (cellUidl.hasAttribute("expanded")) {
  620. expandedWidget = (Widget) child;
  621. expandedWidgetUidl = cellUidl.getChildUIDL(0);
  622. }
  623. }
  624. final ArrayList oldWidgets = getPaintables();
  625. final Iterator oldIt = oldWidgets.iterator();
  626. final Iterator newIt = uidlWidgets.iterator();
  627. final Iterator newUidl = uidl.getChildIterator();
  628. Widget oldChild = null;
  629. while (newIt.hasNext()) {
  630. final Widget child = (Widget) newIt.next();
  631. final UIDL childUidl = ((UIDL) newUidl.next()).getChildUIDL(0);
  632. if (oldChild == null && oldIt.hasNext()) {
  633. // search for next old Paintable which still exists in layout
  634. // and delete others
  635. while (oldIt.hasNext()) {
  636. oldChild = (Widget) oldIt.next();
  637. // now oldChild is an instance of Paintable
  638. if (uidlWidgets.contains(oldChild)) {
  639. break;
  640. } else {
  641. removePaintable((Paintable) oldChild);
  642. oldChild = null;
  643. }
  644. }
  645. }
  646. if (oldChild == null) {
  647. // we are adding components to layout
  648. add(child);
  649. } else if (child == oldChild) {
  650. // child already attached and updated
  651. oldChild = null;
  652. } else if (hasChildComponent(child)) {
  653. // current child has been moved, re-insert before current
  654. // oldChild
  655. // TODO this might be optimized by moving only container element
  656. // to correct position
  657. removeCaption(child);
  658. int index = getWidgetIndex(oldChild);
  659. if (componentToCaption.containsKey(oldChild)) {
  660. index--;
  661. }
  662. remove(child);
  663. insert(child, index);
  664. } else {
  665. // insert new child before old one
  666. final int index = getWidgetIndex(oldChild);
  667. insert(child, index);
  668. }
  669. if (child != expandedWidget) {
  670. ((Paintable) child).updateFromUIDL(childUidl, client);
  671. }
  672. }
  673. // remove possibly remaining old Paintable object which were not updated
  674. while (oldIt.hasNext()) {
  675. oldChild = (Widget) oldIt.next();
  676. final Paintable p = (Paintable) oldChild;
  677. if (!uidlWidgets.contains(p)) {
  678. removePaintable(p);
  679. }
  680. }
  681. if (uidlWidgets.size() == 0) {
  682. return;
  683. }
  684. // Set component alignments
  685. handleAlignments(uidl);
  686. iLayout();
  687. /*
  688. * Expanded widget is updated after layout function so it has its
  689. * container fixed at the moment of updateFromUIDL.
  690. */
  691. if (expandedWidget != null) {
  692. ((Paintable) expandedWidget).updateFromUIDL(expandedWidgetUidl,
  693. client);
  694. // setting overflow auto lazy, not to disturb possible layout
  695. // functions
  696. DOM.setStyleAttribute(DOM.getParent(expandedWidget.getElement()),
  697. "overflow", "auto");
  698. /*
  699. * If a caption has been added we need to recalculate the space
  700. * available for the component
  701. */
  702. getWidgetWrapperFor(expandedWidget).setExpandedSize(
  703. spaceForExpandedWidget);
  704. }
  705. // workaround for safari bug #1870
  706. float wkv = BrowserInfo.get().getWebkitVersion();
  707. if (wkv > 0 && wkv < 526.9) {
  708. DeferredCommand.addCommand(new Command() {
  709. public void execute() {
  710. iLayout();
  711. }
  712. });
  713. }
  714. rendering = false;
  715. }
  716. public boolean requestLayout(Set<Paintable> child) {
  717. if (height != null && width != null) {
  718. /*
  719. * If the height and width has been specified for this container the
  720. * child components cannot make the size of the layout change
  721. */
  722. return true;
  723. }
  724. if (renderInformation.updateSize(getElement())) {
  725. /*
  726. * Size has changed so we let the child components know about the
  727. * new size.
  728. */
  729. iLayout();
  730. return false;
  731. } else {
  732. /*
  733. * Size has not changed so we do not need to propagate the event
  734. * further
  735. */
  736. return true;
  737. }
  738. }
  739. public RenderSpace getAllocatedSpace(Widget child) {
  740. int width = 0, height = 0;
  741. if (orientationMode == ORIENTATION_HORIZONTAL) {
  742. height = renderInformation.getContentAreaSize().getHeight();
  743. if (child == expandedWidget) {
  744. width = spaceForExpandedWidget;
  745. }
  746. } else {
  747. // VERTICAL
  748. width = renderInformation.getContentAreaSize().getWidth();
  749. if (child == expandedWidget) {
  750. height = spaceForExpandedWidget;
  751. ICaption caption = componentToCaption.get(child);
  752. if (caption != null) {
  753. height -= caption.getOffsetHeight();
  754. }
  755. }
  756. }
  757. return new RenderSpace(width, height, child == expandedWidget);
  758. }
  759. }