Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.itmill.toolkit.terminal.gwt.client.ui;
  5. import java.util.HashMap;
  6. import java.util.Iterator;
  7. import java.util.Vector;
  8. import com.google.gwt.user.client.DOM;
  9. import com.google.gwt.user.client.Element;
  10. import com.google.gwt.user.client.ui.Panel;
  11. import com.google.gwt.user.client.ui.Widget;
  12. import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
  13. import com.itmill.toolkit.terminal.gwt.client.BrowserInfo;
  14. import com.itmill.toolkit.terminal.gwt.client.Container;
  15. import com.itmill.toolkit.terminal.gwt.client.ContainerResizedListener;
  16. import com.itmill.toolkit.terminal.gwt.client.ICaption;
  17. import com.itmill.toolkit.terminal.gwt.client.Paintable;
  18. import com.itmill.toolkit.terminal.gwt.client.UIDL;
  19. import com.itmill.toolkit.terminal.gwt.client.Util;
  20. import com.itmill.toolkit.terminal.gwt.client.Util.WidgetSpaceAllocator;
  21. /**
  22. * Full implementation of OrderedLayout client peer.
  23. *
  24. * This class implements all features of OrderedLayout. It currently only
  25. * supports use through UIDL updates. Direct client side use is not (currently)
  26. * suported in all operation modes.
  27. *
  28. *
  29. * <h2>Features</h2>
  30. *
  31. * <h3>Orientation</h3>
  32. *
  33. * <p>
  34. * Orientation of the ordered layout declared whether the children are layouted
  35. * horizontally or vertically.
  36. * </p>
  37. *
  38. * <img src="doc-files/IOrderedLayout_horizontal.png"/> <img
  39. * src="doc-files/IOrderedLayout_vertical.png"/>
  40. *
  41. * <h3>Spacing</h3>
  42. *
  43. * <p>
  44. * Spacing determines if there should be space between the children. Note that
  45. * this does not imply margin.
  46. * </p>
  47. *
  48. * <img src="doc-files/IOrderedLayout_horizontal_spacing.png"/> <img
  49. * src="doc-files/IOrderedLayout_vertical_spacing.png"/>
  50. *
  51. * <h3>Margin</h3>
  52. *
  53. * <p>
  54. * Margin determines if there should be margin around children. Note that this
  55. * does not imply spacing.
  56. * </p>
  57. *
  58. * <img src="doc-files/IOrderedLayout_margin.png"/>
  59. *
  60. * <h3>Positioning the caption, icon, required indicator and error</h3>
  61. *
  62. * <p>
  63. * If the child lets the layout to handle captions, by icon, caption, required
  64. * marker (*) and error icon are placed on top of the component area. Icon will
  65. * be first and is followed by the caption. Required marker is placed right
  66. * after the caption text and error icon is placed last. Note that all of these
  67. * are optional:
  68. * </p>
  69. *
  70. * <img src="doc-files/IOrderedLayout_normal_caption.png"/>
  71. *
  72. * <p>
  73. * If the child lets the layout to handle captions, but the caption and icon are
  74. * both missing, no line is reserved for the required marker (*) and error icon.
  75. * Instead they are placed on the right side of the top of the component area.
  76. * Required marker is placed right after the component text and error icon is
  77. * placed last. If the component is tall, the indicators are aligned along the
  78. * top of the component. Note that both of these indicators are optional:
  79. * </p>
  80. *
  81. * <img src="doc-files/IOrderedLayout_no_caption.png"/>
  82. *
  83. * <p>
  84. * In case the child want to handle the caption by itself, layout does not
  85. * repeat the caption.
  86. * </p>
  87. *
  88. * <img src="doc-files/IOrderedLayout_component_handles_the_caption.png"/>
  89. *
  90. * <h3>Aligning the children</h3>
  91. *
  92. * <p>
  93. * The children of the layout can be aligned horizontally and vertically:
  94. * </p>
  95. *
  96. * <img src="doc-files/IOrderedLayout_alignment.png"/>
  97. *
  98. * <h3>Fixed height, width or both</h3>
  99. *
  100. * <p>
  101. * When no size is explicitly specified, the size of the layout depends on the
  102. * size of its children. If the size if specified, either explicitly or as
  103. * percertages of the parent size, the size is equally divided between the
  104. * children. In case some children might overflow out of the given space, they
  105. * are cut to fit the given space. Note that the size can be independently
  106. * specified for horizontal and vertical dimensions and is independent of the
  107. * orientation. For example, layout can be horizontal and have fixed 300px
  108. * height, but still measure its width from the child sizes.
  109. * </p>
  110. *
  111. * <p>
  112. * Horizontal layout with fixed width of 300px and height of 150px:
  113. * </p>
  114. * <img src="doc-files/IOrderedLayout_w300_h150.png"/>
  115. *
  116. * <p>
  117. * Horizontal layout with fixed width of 300px:
  118. * </p>
  119. * <img src="doc-files/IOrderedLayout_w300.png"/>
  120. *
  121. * <p>
  122. * Horizontal layout with fixed height of 150px:
  123. * </p>
  124. * <img src="doc-files/IOrderedLayout_h150.png"/>
  125. *
  126. *
  127. * <h3>CSS attributes</h3>
  128. *
  129. * <p>
  130. * Sizes for marginals and spacing can be specified for the ordered layout in
  131. * CSS. For example, here are the defaults for OrderedLayout:
  132. * </p>
  133. *
  134. * <pre>
  135. * .i-orderedlayout-margin-top {
  136. * padding-top: 15px;
  137. * }
  138. * .i-orderedlayout-margin-right {
  139. * padding-right: 18px;
  140. * }
  141. * .i-orderedlayout-margin-bottom {
  142. * padding-bottom: 15px;
  143. * }
  144. * .i-orderedlayout-margin-left {
  145. * padding-left: 18px;
  146. * }
  147. *
  148. * .i-orderedlayout-vspacing {
  149. * margin-top: 8px;
  150. * }
  151. * .i-orderedlayout-hspacing {
  152. * padding-left: 8px;
  153. * }
  154. * </pre>
  155. *
  156. * <p>
  157. * When a style-name is set for the layout, this name is included in the style.
  158. * Note that the unspecified dimensions still default to the values given for
  159. * the layout without style. For example, if we would like to give each layout
  160. * with "tested-layout" style quite a bit larger right margin:
  161. * </p>
  162. *
  163. * <pre>
  164. * .i-orderedlayout-tested-layout-margin-right {
  165. * padding-right: 100px;
  166. * }
  167. * </pre>
  168. *
  169. * <p>
  170. * Here is the rendering with getMargin(true). Note that all the other margins
  171. * are set to the default values defined for the layout without stylename:
  172. * </p>
  173. * <img src="doc-files/IOrderedLayout_special-margin.png"/>
  174. *
  175. *
  176. * <h3>DOM-structure</h3>
  177. *
  178. * Note that DOM-structure is an implementation specific and might change in the
  179. * future versions of IT Mill Toolkit. The purpose of this documentation is to
  180. * to ease reading of the implementation and thus to make implementation of your
  181. * own layouts easier.
  182. *
  183. * <div style="border: 1px solid black; padding: 3px;">OUTERDIV
  184. *
  185. * <div style="border: 1px solid black; padding: 3px;">Optional STRUCTURE
  186. *
  187. * <div style="border: 1px solid black; padding: 3px;">CHILDWRAPPER (for each
  188. * child)
  189. *
  190. * <div style="border: 1px solid black; padding: 3px;">Optional ALIGNMENTWRAPPER
  191. *
  192. * <div style="border: 1px solid black; padding: 3px;">Optional CLIPPER
  193. *
  194. * <div style="border: 1px solid black; padding: 3px;">CAPTION <span
  195. * style="border: 1px solid black; padding: 3px;">ICON-IMG</span> <span
  196. * style="border: 1px solid black; padding: 3px;">CAPTION-SPAN</span> <span
  197. * style="border: 1px solid black; padding: 3px;">REQUIRED-SPAN</span> <span
  198. * style="border: 1px solid black; padding: 3px;">ERRORINDICATOR-DIV</span>
  199. * </div>
  200. *
  201. * <div style="border: 1px solid black; padding: 3px; margin-top:3px;">Widget
  202. * component</div>
  203. *
  204. * </div></div></div>
  205. *
  206. * </div></div>
  207. *
  208. * <p>
  209. * Notes:
  210. * <ul>
  211. * <li>If caption and icon are missing from child, <i>Widget component</i> and
  212. * <i>CAPTION</i> elements are swithched</li>
  213. * <li>If either child manages caption, or it has no caption, icon, required or
  214. * error, <i>CAPTION</i> element is not needed at all</li>
  215. * <li>If layout is vertical and its width is specified, <i>Optional
  216. * STRUCTURE</i> is not present. Otherwise it looks like <div
  217. * style="border: 1px solid black; padding: 3px;">TABLE <div
  218. * style="border: 1px solid black; padding: 3px;">TBODY <div
  219. * style="border: 1px solid black; padding: 3px;">Optional TR only included in
  220. * case of horizontal layouts </div></div></div></li>
  221. * <li><i>CHILDWRAPPER</i> is a DIV in case of the layout is vertical and width
  222. * is specified. For vertical layouts with unknown width it is TR-TD. For
  223. * horizontal layouts, it is TR-TD.</li>
  224. * <li><i>Optionasl ALIGNMENTWRAPPER</i> are only used alignment is not the
  225. * default - top-left. Alignment wrapper structure is
  226. * TABLE-TBODY-TR-TD-TABLE-TBODY-TR-TD, where the outer table td is used to
  227. * specify the alignments and inner table td to reset the table defaults to
  228. * top-left.</li>
  229. * <li><i>Optional CLIPPERDIV</i> included in the structure only if alignment
  230. * structure is in place and <i>CHILDWRAPPER</i> is not a div and thus can not
  231. * be used for clipping</li>
  232. * </ul>
  233. * </p>
  234. *
  235. *
  236. * @author IT Mill Ltd
  237. */
  238. public class IOrderedLayout extends Panel implements Container,
  239. ContainerResizedListener, WidgetSpaceAllocator {
  240. public static final String CLASSNAME = "i-orderedlayout";
  241. public static final int ORIENTATION_VERTICAL = 0;
  242. public static final int ORIENTATION_HORIZONTAL = 1;
  243. /**
  244. * If margin and spacing values has been calculated, this holds the values
  245. * for the given UIDL style attribute .
  246. */
  247. private static HashMap measuredMargins = new HashMap();
  248. /**
  249. * Spacing. Correct values will be set in
  250. * updateMarginAndSpacingFromCSS(UIDL)
  251. */
  252. private int hSpacing, vSpacing;
  253. /**
  254. * Margin. Correct values will be set in updateMarginAndSpacingFromCSS(UIDL)
  255. */
  256. private int marginTop, marginBottom, marginLeft, marginRight;
  257. int orientationMode = ORIENTATION_VERTICAL;
  258. protected ApplicationConnection client;
  259. /**
  260. * Reference to Element where wrapped childred are contained. Normally a
  261. * DIV, TR or a TBODY element.
  262. */
  263. private Element wrappedChildContainer;
  264. /**
  265. * List of child widgets. This is not the list of wrappers, but the actual
  266. * widgets
  267. */
  268. private final Vector childWidgets = new Vector();
  269. /**
  270. * In table mode, the root element is table instead of div.
  271. */
  272. private boolean tableMode = false;
  273. /**
  274. * Root element. This element points to the outmost table-element (in table
  275. * mode) or outmost div (in non-table-mode). This non-table-mode this equals
  276. * to the getElement().
  277. */
  278. private Element root = null;
  279. /**
  280. * Last set width of the component. Null if undefined (instead of being "").
  281. */
  282. private String width = null;
  283. /**
  284. * Last set height of the component. Null if undefined (instead of being
  285. * "").
  286. */
  287. private String height = null;
  288. /**
  289. * List of child widget wrappers. These wrappers are in exact same indexes
  290. * as the widgets in childWidgets list.
  291. */
  292. private final Vector<WidgetWrapper> childWidgetWrappers = new Vector<WidgetWrapper>();
  293. /** Whether the component has spacing enabled. */
  294. private boolean hasComponentSpacing;
  295. /** Information about margin states. */
  296. private MarginInfo margins = new MarginInfo(0);
  297. /**
  298. * Flag that indicates that the child layouts must be updated as soon as
  299. * possible. This will be done in the end of updateFromUIDL.
  300. */
  301. private boolean childLayoutsHaveChanged = false;
  302. private int renderedHeight;
  303. private int renderedWidth;
  304. /**
  305. * Construct the DOM of the orderder layout.
  306. *
  307. * <p>
  308. * There are two modes - vertical and horizontal.
  309. * <ul>
  310. * <li>Vertical mode uses structure: div-root ( div-wrap ( child ) div-wrap
  311. * ( child ))).</li>
  312. * <li>Horizontal mode uses structure: table ( tbody ( tr-childcontainer (
  313. * td-wrap ( child ) td-wrap ( child) )) )</li>
  314. * </ul>
  315. * where root and childcontainer refer to the root element and the element
  316. * that contain WidgetWrappers.
  317. * </p>
  318. *
  319. */
  320. public IOrderedLayout() {
  321. wrappedChildContainer = root = DOM.createDiv();
  322. setElement(wrappedChildContainer);
  323. setStyleName(CLASSNAME);
  324. }
  325. /**
  326. * Update orientation, if it has changed.
  327. *
  328. * @param newOrientationMode
  329. */
  330. private void rebuildRootDomStructure(int oldOrientationMode) {
  331. // Should we have table as a root element?
  332. boolean newTableMode = !(orientationMode == ORIENTATION_VERTICAL && width != null);
  333. // Already in correct mode?
  334. if (oldOrientationMode == orientationMode && newTableMode == tableMode) {
  335. return;
  336. }
  337. boolean oldTableMode = tableMode;
  338. tableMode = newTableMode;
  339. /*
  340. * If the child are not detached before the parent is cleared with
  341. * setInnerHTML the children will also be cleared in IE
  342. */
  343. if (BrowserInfo.get().isIE()) {
  344. while (true) {
  345. Element child = DOM.getFirstChild(getElement());
  346. if (child != null) {
  347. DOM.removeChild(getElement(), child);
  348. } else {
  349. break;
  350. }
  351. }
  352. }
  353. // Constuct base DOM-structure and clean any already attached
  354. // widgetwrappers from DOM.
  355. if (tableMode) {
  356. final String structure = "<table cellspacing=\"0\" cellpadding=\"0\"><tbody>"
  357. + (orientationMode == ORIENTATION_HORIZONTAL ? "<tr valign=\"top\"></tr>"
  358. : "") + "</tbody></table>";
  359. DOM.setInnerHTML(getElement(), structure);
  360. root = DOM.getFirstChild(getElement());
  361. // set TBODY to be the wrappedChildContainer
  362. wrappedChildContainer = DOM.getFirstChild(root);
  363. // In case of horizontal layouts, we must user TR instead of TBODY
  364. if (orientationMode == ORIENTATION_HORIZONTAL) {
  365. wrappedChildContainer = DOM
  366. .getFirstChild(wrappedChildContainer);
  367. }
  368. } else {
  369. root = wrappedChildContainer = getElement();
  370. DOM.setInnerHTML(getElement(), "");
  371. }
  372. // Reinsert all widget wrappers to this container
  373. final int currentOrientationMode = orientationMode;
  374. for (int i = 0; i < childWidgetWrappers.size(); i++) {
  375. WidgetWrapper wr = childWidgetWrappers.get(i);
  376. orientationMode = oldOrientationMode;
  377. tableMode = oldTableMode;
  378. Element oldWrElement = wr.getElementWrappingWidgetAndCaption();
  379. orientationMode = currentOrientationMode;
  380. tableMode = newTableMode;
  381. String classe = DOM.getElementAttribute(oldWrElement, "class");
  382. wr.resetRootElement();
  383. Element newWrElement = wr.getElementWrappingWidgetAndCaption();
  384. if (classe != null && classe.length() > 0) {
  385. DOM.setElementAttribute(newWrElement, "class", classe);
  386. }
  387. while (DOM.getChildCount(oldWrElement) > 0) {
  388. Element c = DOM.getFirstChild(oldWrElement);
  389. DOM.removeChild(oldWrElement, c);
  390. DOM.appendChild(newWrElement, c);
  391. }
  392. DOM.appendChild(wrappedChildContainer, wr.getElement());
  393. }
  394. // Update child layouts
  395. childLayoutsHaveChanged = true;
  396. }
  397. /** Update the contents of the layout from UIDL. */
  398. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  399. this.client = client;
  400. // Only non-cached UIDL:s can introduce changes
  401. if (uidl.getBooleanAttribute("cached")) {
  402. return;
  403. }
  404. updateMarginAndSpacingSizesFromCSS(uidl);
  405. // Update sizes, ...
  406. if (client.updateComponent(this, uidl, true)) {
  407. return;
  408. }
  409. // Rebuild DOM tree root if necessary
  410. int oldO = orientationMode;
  411. orientationMode = "horizontal".equals(uidl
  412. .getStringAttribute("orientation")) ? ORIENTATION_HORIZONTAL
  413. : ORIENTATION_VERTICAL;
  414. rebuildRootDomStructure(oldO);
  415. // Handle component spacing later in handleAlignments() method
  416. hasComponentSpacing = uidl.getBooleanAttribute("spacing");
  417. // Collect the list of contained widgets after this update
  418. final Vector newWidgets = new Vector();
  419. for (final Iterator it = uidl.getChildIterator(); it.hasNext();) {
  420. final UIDL uidlForChild = (UIDL) it.next();
  421. final Paintable child = client.getPaintable(uidlForChild);
  422. newWidgets.add(child);
  423. }
  424. // Iterator for old widgets
  425. final Iterator oldWidgetsIterator = (new Vector(childWidgets))
  426. .iterator();
  427. // Iterator for new widgets
  428. final Iterator newWidgetsIterator = newWidgets.iterator();
  429. // Iterator for new UIDL
  430. final Iterator newUIDLIterator = uidl.getChildIterator();
  431. // List to collect all now painted widgets to in order to remove
  432. // unpainted ones later
  433. final Vector paintedWidgets = new Vector();
  434. final Vector childsToPaint = new Vector();
  435. // Add any new widgets to the ordered layout
  436. Widget oldChild = null;
  437. while (newWidgetsIterator.hasNext()) {
  438. final Widget newChild = (Widget) newWidgetsIterator.next();
  439. final UIDL newChildUIDL = (UIDL) newUIDLIterator.next();
  440. // Remove any unneeded old widgets
  441. if (oldChild == null && oldWidgetsIterator.hasNext()) {
  442. // search for next old Paintable which still exists in layout
  443. // and delete others
  444. while (oldWidgetsIterator.hasNext()) {
  445. oldChild = (Widget) oldWidgetsIterator.next();
  446. // now oldChild is an instance of Paintable
  447. if (paintedWidgets.contains(oldChild)) {
  448. continue;
  449. } else if (newWidgets.contains(oldChild)) {
  450. break;
  451. } else {
  452. remove(oldChild);
  453. oldChild = null;
  454. }
  455. }
  456. }
  457. if (oldChild == null) {
  458. // we are adding components to the end of layout
  459. add(newChild);
  460. } else if (newChild == oldChild) {
  461. // child already attached in correct position
  462. oldChild = null;
  463. } else if (hasChildComponent(newChild)) {
  464. // current child has been moved, re-insert before current
  465. // oldChild
  466. add(newChild, childWidgets.indexOf(oldChild));
  467. } else {
  468. // insert new child before old one
  469. add(newChild, childWidgets.indexOf(oldChild));
  470. }
  471. // Update the child component
  472. childsToPaint.add(new Object[] { newChild, newChildUIDL });
  473. // Add this newly handled component to the list of painted
  474. // components
  475. paintedWidgets.add(newChild);
  476. }
  477. // Remove possibly remaining old widgets which were not in painted UIDL
  478. while (oldWidgetsIterator.hasNext()) {
  479. oldChild = (Widget) oldWidgetsIterator.next();
  480. if (!newWidgets.contains(oldChild)) {
  481. remove(oldChild);
  482. }
  483. }
  484. // Handle component alignments
  485. handleAlignmentsSpacingAndMargins(uidl);
  486. // Reset sizes for the children
  487. updateChildSizes(-1, -1);
  488. // Paint children
  489. for (int i = 0; i < childsToPaint.size(); i++) {
  490. Object[] t = (Object[]) childsToPaint.get(i);
  491. ((Paintable) t[0]).updateFromUIDL((UIDL) t[1], client);
  492. }
  493. // Update child layouts
  494. // TODO This is most probably unnecessary and should be done within
  495. // update Child H/W
  496. if (childLayoutsHaveChanged) {
  497. Util.runDescendentsLayout(this);
  498. childLayoutsHaveChanged = false;
  499. }
  500. /* Store the rendered size so we later can see if it has changed */
  501. renderedWidth = root.getOffsetWidth();
  502. renderedHeight = root.getOffsetHeight();
  503. }
  504. private void updateMarginAndSpacingSizesFromCSS(UIDL uidl) {
  505. // Style for this layout
  506. String style = uidl.getStringAttribute("style");
  507. if (style == null) {
  508. style = "";
  509. }
  510. // Try to find measured from cache
  511. int[] r = (int[]) measuredMargins.get(style);
  512. // Measure from DOM
  513. if (r == null) {
  514. r = new int[] { 0, 0, 0, 0, 0, 0 };
  515. // Construct DOM for measurements
  516. Element e1 = DOM.createTable();
  517. DOM.setStyleAttribute(e1, "position", "absolute");
  518. DOM.setElementProperty(e1, "cellSpacing", "0");
  519. DOM.setElementProperty(e1, "cellPadding", "0");
  520. Element e11 = DOM.createTBody();
  521. Element e12 = DOM.createTR();
  522. Element e13 = DOM.createTD();
  523. Element e2 = DOM.createDiv();
  524. Element e3 = DOM.createDiv();
  525. DOM.setStyleAttribute(e3, "width", "100px");
  526. DOM.setStyleAttribute(e3, "height", "100px");
  527. DOM.appendChild(getElement(), e1);
  528. DOM.appendChild(e1, e11);
  529. DOM.appendChild(e11, e12);
  530. DOM.appendChild(e12, e13);
  531. DOM.appendChild(e13, e2);
  532. DOM.appendChild(e2, e3);
  533. DOM.setInnerText(e3, ".");
  534. // Measure different properties
  535. final String[] classes = { "margin-top", "margin-right",
  536. "margin-bottom", "margin-left", "vspacing", "hspacing" };
  537. for (int c = 0; c < 6; c++) {
  538. StringBuffer styleBuf = new StringBuffer();
  539. final String primaryName = getStylePrimaryName();
  540. styleBuf.append(primaryName + "-" + classes[c]);
  541. if (style.length() > 0) {
  542. final String[] styles = style.split(" ");
  543. for (int i = 0; i < styles.length; i++) {
  544. styleBuf.append(" ");
  545. styleBuf.append(primaryName);
  546. styleBuf.append("-");
  547. styleBuf.append(styles[i]);
  548. styleBuf.append("-");
  549. styleBuf.append(classes[c]);
  550. }
  551. }
  552. DOM.setElementProperty(e2, "className", styleBuf.toString());
  553. // Measure
  554. r[c] = DOM.getElementPropertyInt(e1,
  555. (c % 2) == 1 ? "offsetWidth" : "offsetHeight") - 100;
  556. }
  557. // Clean-up
  558. DOM.removeChild(getElement(), e1);
  559. // Cache for further use
  560. measuredMargins.put(style, r);
  561. }
  562. // Set the properties
  563. marginTop = r[0];
  564. marginRight = r[1];
  565. marginBottom = r[2];
  566. marginLeft = r[3];
  567. vSpacing = r[4];
  568. hSpacing = r[5];
  569. }
  570. /**
  571. * While setting width, ensure that margin div is also resized properly.
  572. * Furthermore, enable/disable fixed mode
  573. */
  574. public void setWidth(String newWidth) {
  575. width = newWidth == null || "".equals(newWidth) ? null : newWidth;
  576. // As we use divs at root - for them using 100% width should be
  577. // calculated with ""
  578. super.setWidth("");
  579. // Update child layouts
  580. childLayoutsHaveChanged = true;
  581. }
  582. /**
  583. * While setting height, ensure that margin div is also resized properly.
  584. * Furthermore, enable/disable fixed mode
  585. */
  586. public void setHeight(String newHeight) {
  587. super.setHeight(newHeight);
  588. height = newHeight == null || "".equals(newHeight) ? null : newHeight;
  589. // Update child layouts
  590. childLayoutsHaveChanged = true;
  591. }
  592. /** Recalculate and apply the space given for each child in this layout. */
  593. private void updateChildSizes(int renderedWidth, int renderedHeight) {
  594. int numChild = childWidgets.size();
  595. int childHeightTotal = -1;
  596. int childHeightDivisor = 1;
  597. int childWidthTotal = -1;
  598. int childWidthDivisor = 1;
  599. // Vertical layout is calculated by us
  600. if (height != null) {
  601. // Calculate the space for fixed contents minus marginals
  602. if (tableMode) {
  603. // If we know explicitly set pixel-size, use that
  604. if (height.endsWith("px")) {
  605. try {
  606. childHeightTotal = Integer.parseInt(height.substring(0,
  607. height.length() - 2));
  608. // For negative sizes, use measurements
  609. if (childHeightTotal < 0) {
  610. childHeightTotal = rootOffsetMeasure("offsetHeight");
  611. }
  612. } catch (NumberFormatException e) {
  613. // In case of invalid number, try to measure the size;
  614. childHeightTotal = rootOffsetMeasure("offsetHeight");
  615. }
  616. } else if (height.endsWith("%") && renderedHeight >= 0) {
  617. // If we have a relative height and know how large we are we
  618. // can
  619. // simply use that
  620. childWidthTotal = renderedHeight;
  621. } else {
  622. // If not pixels, nor percentage, try to measure the size
  623. childHeightTotal = rootOffsetMeasure("offsetHeight");
  624. }
  625. } else {
  626. childHeightTotal = DOM.getElementPropertyInt(getElement(),
  627. "offsetHeight");
  628. }
  629. childHeightTotal -= margins.hasTop() ? marginTop : 0;
  630. childHeightTotal -= margins.hasBottom() ? marginBottom : 0;
  631. // Reduce spacing from the size
  632. if (hasComponentSpacing) {
  633. childHeightTotal -= ((orientationMode == ORIENTATION_HORIZONTAL) ? hSpacing
  634. : vSpacing)
  635. * (numChild - 1);
  636. }
  637. // Total space is divided among the children
  638. if (orientationMode == ORIENTATION_VERTICAL) {
  639. childHeightDivisor = numChild;
  640. }
  641. }
  642. // layout is calculated by us
  643. if (width != null) {
  644. // Calculate the space for fixed contents minus marginals
  645. // If we know explicitly set pixel-size, use that
  646. if (width.endsWith("px")) {
  647. try {
  648. childWidthTotal = Integer.parseInt(width.substring(0, width
  649. .length() - 2));
  650. // For negative sizes, use measurements
  651. if (childWidthTotal < 0) {
  652. childWidthTotal = rootOffsetMeasure("offsetWidth");
  653. }
  654. } catch (NumberFormatException e) {
  655. // In case of invalid number, try to measure the size;
  656. childWidthTotal = rootOffsetMeasure("offsetWidth");
  657. }
  658. } else if (width.endsWith("%") && renderedWidth >= 0) {
  659. // If we have a relative width and know how large we are we can
  660. // simply use that
  661. childWidthTotal = renderedWidth;
  662. } else {
  663. // If not pixels, nor percentage, try to measure the size
  664. childWidthTotal = rootOffsetMeasure("offsetWidth");
  665. }
  666. childWidthTotal -= margins.hasLeft() ? marginLeft : 0;
  667. childWidthTotal -= margins.hasRight() ? marginRight : 0;
  668. // Reduce spacing from the size
  669. if (hasComponentSpacing
  670. && orientationMode == ORIENTATION_HORIZONTAL) {
  671. childWidthTotal -= hSpacing * (numChild - 1);
  672. }
  673. // Total space is divided among the children
  674. if (orientationMode == ORIENTATION_HORIZONTAL) {
  675. childWidthDivisor = numChild;
  676. }
  677. }
  678. // Set the sizes for each child
  679. for (Iterator i = childWidgetWrappers.iterator(); i.hasNext();) {
  680. int w, h;
  681. if (childHeightDivisor > 1) {
  682. h = Math.round(((float) childHeightTotal)
  683. / (childHeightDivisor--));
  684. childHeightTotal -= h;
  685. } else {
  686. h = childHeightTotal;
  687. }
  688. if (childWidthDivisor > 1) {
  689. w = Math.round(((float) childWidthTotal)
  690. / (childWidthDivisor--));
  691. childWidthTotal -= w;
  692. } else {
  693. w = childWidthTotal;
  694. }
  695. WidgetWrapper ww = (WidgetWrapper) i.next();
  696. ww.forceSize(w, h);
  697. }
  698. }
  699. /**
  700. * Measure how much space the root element could get.
  701. *
  702. * This measures the space allocated by the parent for the root element
  703. * without letting root element to affect the calculation.
  704. *
  705. * @param offset
  706. * offsetWidth or offsetHeight
  707. */
  708. private int rootOffsetMeasure(String offset) {
  709. // TODO This method must be optimized!
  710. Element measure = DOM.createDiv();
  711. DOM.setStyleAttribute(measure, "height", "100%");
  712. Element parent = DOM.getParent(getElement());
  713. DOM.insertBefore(parent, measure, getElement());
  714. DOM.removeChild(parent, getElement());
  715. int size = DOM.getElementPropertyInt(measure, offset);
  716. DOM.insertBefore(parent, getElement(), measure);
  717. DOM.removeChild(parent, measure);
  718. // In case the no space would be given for this element
  719. // without pushing, use the current side of the root
  720. return size;
  721. }
  722. /** Parse alignments from UIDL and pass whem to correct widgetwrappers */
  723. private void handleAlignmentsSpacingAndMargins(UIDL uidl) {
  724. // Only update margins when they have changed
  725. // TODO this should be optimized to avoid reupdating these
  726. margins = new MarginInfo(uidl.getIntAttribute("margins"));
  727. // Component alignments as a comma separated list.
  728. // See com.itmill.toolkit.terminal.gwt.client.ui.AlignmentInfo.java for
  729. // possible values.
  730. final int[] alignments = uidl.getIntArrayAttribute("alignments");
  731. int alignmentIndex = 0;
  732. // Insert alignment attributes
  733. final Iterator it = childWidgetWrappers.iterator();
  734. while (it.hasNext()) {
  735. // Calculate alignment info
  736. final AlignmentInfo ai = new AlignmentInfo(
  737. alignments[alignmentIndex++]);
  738. final WidgetWrapper wr = (WidgetWrapper) it.next();
  739. wr.setAlignment(ai.getVerticalAlignment(), ai
  740. .getHorizontalAlignment());
  741. // Handle spacing and margins in this loop as well
  742. wr.setSpacingAndMargins(alignmentIndex == 1,
  743. alignmentIndex == alignments.length);
  744. }
  745. }
  746. /**
  747. * Wrapper around single child in the layout.
  748. *
  749. * This helper also manages spacing, margins and alignment for individual
  750. * cells handling. It also can put hard size limits for its contens by
  751. * clipping the content to given pixel size.
  752. *
  753. */
  754. class WidgetWrapper {
  755. /**
  756. * When alignment table structure is used, these elements correspond to
  757. * the TD elements within the structure. If alignment is not used, these
  758. * are null.
  759. */
  760. Element alignmentTD, innermostTDinAlignmnetStructure;
  761. /**
  762. * When clipping must be done and the element wrapping clipped content
  763. * would be TD instead of DIV, this element points to additional DIV
  764. * that is used for clipping.
  765. */
  766. Element clipperDiv;
  767. /** Caption element when used. */
  768. ICaption caption = null;
  769. /**
  770. * Last set pixel height for the wrapper. -1 if vertical clipping is not
  771. * used.
  772. */
  773. int lastForcedPixelHeight = -1;
  774. /**
  775. * Last set pidel width for the wrapper. -1 if horizontal clipping is
  776. * not used.
  777. */
  778. int lastForcedPixelWidth = -1;
  779. int horizontalPadding = 0, verticalPadding = 0;
  780. /** Widget Wrapper root element */
  781. Element wrapperElement;
  782. /** Set the root element */
  783. public WidgetWrapper() {
  784. resetRootElement();
  785. }
  786. public Element getElement() {
  787. return wrapperElement;
  788. }
  789. /**
  790. * Set the width and height given for the wrapped widget in pixels.
  791. *
  792. * -1 if unconstrained.
  793. */
  794. public void forceSize(int pixelWidth, int pixelHeight) {
  795. // If we are already at the correct size, do nothing
  796. if (lastForcedPixelHeight == pixelHeight
  797. && lastForcedPixelWidth == pixelWidth) {
  798. return;
  799. }
  800. // Clipper DIV is needed?
  801. if (tableMode && (pixelHeight >= 0 || pixelWidth >= 0)) {
  802. if (clipperDiv == null) {
  803. createClipperDiv();
  804. }
  805. }
  806. // ClipperDiv is not needed, remove if necessary
  807. else if (clipperDiv != null) {
  808. removeClipperDiv();
  809. }
  810. Element e = clipperDiv != null ? clipperDiv
  811. : getElementWrappingAlignmentStructures();
  812. // Overflow
  813. DOM.setStyleAttribute(e, "overflow", pixelWidth < 0
  814. && pixelHeight < 0 ? "" : "hidden");
  815. // Set size
  816. DOM.setStyleAttribute(e, "width", pixelWidth < 0 ? "" : pixelWidth
  817. + "px");
  818. DOM.setStyleAttribute(e, "height",
  819. pixelHeight < 0 ? (e == clipperDiv ? "100%" : "")
  820. : pixelHeight + "px");
  821. // Set cached values
  822. lastForcedPixelWidth = pixelWidth;
  823. lastForcedPixelHeight = pixelHeight;
  824. }
  825. /** Create a DIV for clipping the child */
  826. private void createClipperDiv() {
  827. clipperDiv = DOM.createDiv();
  828. final Element e = getElementWrappingClipperDiv();
  829. String classe = DOM.getElementAttribute(e, "class");
  830. while (DOM.getChildCount(e) > 0) {
  831. final Element c = DOM.getFirstChild(e);
  832. DOM.removeChild(e, c);
  833. DOM.appendChild(clipperDiv, c);
  834. }
  835. if (classe != null && classe.length() > 0) {
  836. DOM.removeElementAttribute(e, "class");
  837. DOM.setElementAttribute(clipperDiv, "class", classe);
  838. }
  839. DOM.appendChild(e, clipperDiv);
  840. }
  841. /** Undo createClipperDiv() */
  842. private void removeClipperDiv() {
  843. final Element e = getElementWrappingClipperDiv();
  844. String classe = DOM.getElementAttribute(clipperDiv, "class");
  845. while (DOM.getChildCount(clipperDiv) > 0) {
  846. final Element c = DOM.getFirstChild(clipperDiv);
  847. DOM.removeChild(clipperDiv, c);
  848. DOM.appendChild(e, c);
  849. }
  850. DOM.removeChild(e, clipperDiv);
  851. clipperDiv = null;
  852. if (classe != null && classe.length() > 0) {
  853. DOM.setElementAttribute(e, "class", classe);
  854. }
  855. }
  856. /**
  857. * Get the element containing the caption and the wrapped widget.
  858. * Returned element can one of the following:
  859. * <ul>
  860. * <li>(a) Root DIV of the WrapperElement when not in tableMode</li>
  861. * <li>(b) TD in just below the root TR of the WrapperElement when in
  862. * tableMode</li>
  863. * <li>(c) clipperDiv inside the (a) or (b)</li>
  864. * <li>(d) The innermost TD within alignment structures located in (a),
  865. * (b) or (c)</li>
  866. * </ul>
  867. *
  868. * @return Element described above
  869. */
  870. private Element getElementWrappingWidgetAndCaption() {
  871. // When alignment is used, we will can safely return the innermost
  872. // TD
  873. if (innermostTDinAlignmnetStructure != null) {
  874. return innermostTDinAlignmnetStructure;
  875. }
  876. // In all other cases element wrapping the potential alignment
  877. // structures is the correct one
  878. return getElementWrappingAlignmentStructures();
  879. }
  880. /**
  881. * Get the element where alignment structures should be placed in if
  882. * they are in use.
  883. *
  884. * Returned element can one of the following:
  885. * <ul>
  886. * <li>(a) Root DIV of the WrapperElement when not in tableMode</li>
  887. * <li>(b) TD in just below the root TR of the WrapperElement when in
  888. * tableMode</li>
  889. * <li>(c) clipperDiv inside the (a) or (b)</li>
  890. * </ul>
  891. *
  892. * @return Element described above
  893. */
  894. private Element getElementWrappingAlignmentStructures() {
  895. // Clipper DIV wraps the alignment structures if present
  896. if (clipperDiv != null) {
  897. return clipperDiv;
  898. }
  899. // When Clipper DIV is not used, we just give the element
  900. // that would wrap it if it would be used
  901. return getElementWrappingClipperDiv();
  902. }
  903. /**
  904. * Get the element where clipperDiv should be placed in if they it is in
  905. * use.
  906. *
  907. * Returned element can one of the following:
  908. * <ul>
  909. * <li>(a) Root DIV of the WrapperElement when not in tableMode</li>
  910. * <li>(b) TD in just below the root TR of the WrapperElement when in
  911. * tableMode</li>
  912. * </ul>
  913. *
  914. * @return Element described above
  915. */
  916. private Element getElementWrappingClipperDiv() {
  917. // Only vertical layouts in non-table mode use TR as root, for the
  918. // rest we can safely give root element
  919. if (!tableMode || orientationMode == ORIENTATION_HORIZONTAL) {
  920. return wrapperElement;
  921. }
  922. // The root is TR, we'll thus give the TD that is immediately within
  923. // the root
  924. return DOM.getFirstChild(wrapperElement);
  925. }
  926. /**
  927. * Create tr, td or div - depending on the orientation of the layout and
  928. * set it as root.
  929. *
  930. * All contents of the wrapper are cleared. Caller is responsible for
  931. * preserving the contents and moving them into new root.
  932. *
  933. * @return Previous root element.
  934. */
  935. private void resetRootElement() {
  936. // TODO Should we remove the existing element?
  937. if (tableMode) {
  938. if (orientationMode == ORIENTATION_HORIZONTAL) {
  939. wrapperElement = DOM.createTD();
  940. } else {
  941. wrapperElement = DOM.createTR();
  942. DOM.appendChild(wrapperElement, DOM.createTD());
  943. }
  944. } else {
  945. wrapperElement = DOM.createDiv();
  946. // Apply 'hasLayout' for IE (needed to get accurate dimension
  947. // calculations)
  948. if (BrowserInfo.get().isIE()) {
  949. DOM.setStyleAttribute(wrapperElement, "zoom", "1");
  950. }
  951. }
  952. // Clear any references to intermediate elements
  953. clipperDiv = alignmentTD = innermostTDinAlignmnetStructure = null;
  954. }
  955. /** Update the caption of the element contained in this wrapper. */
  956. public void updateCaption(UIDL uidl, Paintable paintable) {
  957. final Widget widget = (Widget) paintable;
  958. final Element captionWrapper = getElementWrappingWidgetAndCaption();
  959. // The widget needs caption
  960. if (ICaption.isNeeded(uidl)) {
  961. // If the caption element is missing, create it
  962. boolean justAdded = false;
  963. if (caption == null) {
  964. justAdded = true;
  965. caption = new ICaption(paintable, client);
  966. }
  967. // Update caption contents
  968. caption.updateCaption(uidl);
  969. final boolean after = caption.shouldBePlacedAfterComponent();
  970. final Element captionElement = caption.getElement();
  971. final Element widgetElement = widget.getElement();
  972. if (justAdded) {
  973. // As the caption has just been created, insert it to DOM
  974. if (after) {
  975. DOM.appendChild(captionWrapper, captionElement);
  976. DOM.setElementAttribute(captionWrapper, "class",
  977. "i-orderedlayout-w");
  978. caption.addStyleName("i-orderedlayout-c");
  979. widget.addStyleName("i-orderedlayout-w-e");
  980. } else {
  981. DOM.insertChild(captionWrapper, captionElement, 0);
  982. }
  983. } else
  984. // Caption exists. Move it to correct position if needed
  985. if (after == (DOM.getChildIndex(captionWrapper, widgetElement) > DOM
  986. .getChildIndex(captionWrapper, captionElement))) {
  987. Element firstElement = DOM.getChild(captionWrapper, DOM
  988. .getChildCount(captionWrapper) - 2);
  989. if (firstElement != null) {
  990. DOM.removeChild(captionWrapper, firstElement);
  991. DOM.appendChild(captionWrapper, firstElement);
  992. }
  993. DOM.setElementAttribute(captionWrapper, "class",
  994. after ? "i-orderedlayout-w" : "");
  995. if (after) {
  996. caption.addStyleName("i-orderedlayout-c");
  997. widget.addStyleName("i-orderedlayout-w-e");
  998. } else {
  999. widget.removeStyleName("i-orderedlayout-w-e");
  1000. caption.removeStyleName("i-orderedlayout-w-c");
  1001. }
  1002. }
  1003. }
  1004. // Caption is not needed
  1005. else {
  1006. // Remove existing caption from DOM
  1007. if (caption != null) {
  1008. DOM.removeChild(captionWrapper, caption.getElement());
  1009. caption = null;
  1010. DOM.setElementAttribute(captionWrapper, "class", "");
  1011. widget.removeStyleName("i-orderedlayout-w-e");
  1012. caption.removeStyleName("i-orderedlayout-w-c");
  1013. }
  1014. }
  1015. }
  1016. /**
  1017. * Set alignments for this wrapper.
  1018. */
  1019. void setAlignment(String verticalAlignment, String horizontalAlignment) {
  1020. // use one-cell table to implement horizontal alignments, only
  1021. // for values other than top-left (which is default)
  1022. if (!horizontalAlignment.equals("left")
  1023. || !verticalAlignment.equals("top")) {
  1024. // The previous positioning has been left (or unspecified).
  1025. // Thus we need to create a one-cell-table to position
  1026. // this element.
  1027. if (alignmentTD == null) {
  1028. // Store and remove the current childs (widget and caption)
  1029. Element c1 = DOM
  1030. .getFirstChild(getElementWrappingWidgetAndCaption());
  1031. if (c1 != null) {
  1032. DOM.removeChild(getElementWrappingWidgetAndCaption(),
  1033. c1);
  1034. }
  1035. Element c2 = DOM
  1036. .getFirstChild(getElementWrappingWidgetAndCaption());
  1037. if (c2 != null) {
  1038. DOM.removeChild(getElementWrappingWidgetAndCaption(),
  1039. c2);
  1040. }
  1041. // Construct table structure to align children
  1042. final String t = "<table cellpadding='0' cellspacing='0' width='100%' height='100%'><tbody><tr><td>"
  1043. + "<table cellpadding='0' cellspacing='0' ><tbody><tr><td align='left'>"
  1044. + "</td></tr></tbody></table></td></tr></tbody></table>";
  1045. DOM.setInnerHTML(getElementWrappingWidgetAndCaption(), t);
  1046. alignmentTD = DOM
  1047. .getFirstChild(DOM
  1048. .getFirstChild(DOM
  1049. .getFirstChild(DOM
  1050. .getFirstChild(getElementWrappingWidgetAndCaption()))));
  1051. innermostTDinAlignmnetStructure = DOM.getFirstChild(DOM
  1052. .getFirstChild(DOM.getFirstChild(DOM
  1053. .getFirstChild(alignmentTD))));
  1054. // Restore children inside the
  1055. if (c1 != null) {
  1056. DOM.appendChild(innermostTDinAlignmnetStructure, c1);
  1057. if (c2 != null) {
  1058. DOM
  1059. .appendChild(
  1060. innermostTDinAlignmnetStructure, c2);
  1061. }
  1062. }
  1063. } else {
  1064. // Go around optimization bug in WebKit and ensure repaint
  1065. if (BrowserInfo.get().isSafari()) {
  1066. String prevValue = DOM.getElementAttribute(alignmentTD,
  1067. "align");
  1068. if (!horizontalAlignment.equals(prevValue)) {
  1069. Element parent = DOM.getParent(alignmentTD);
  1070. DOM.removeChild(parent, alignmentTD);
  1071. DOM.appendChild(parent, alignmentTD);
  1072. }
  1073. }
  1074. }
  1075. // Set the alignment in td
  1076. DOM.setElementAttribute(alignmentTD, "align",
  1077. horizontalAlignment);
  1078. DOM.setElementAttribute(alignmentTD, "valign",
  1079. verticalAlignment);
  1080. } else {
  1081. // In this case we are requested to position this left
  1082. // while as it has had some other position in the past.
  1083. // Thus the one-cell wrapper table must be removed.
  1084. if (alignmentTD != null) {
  1085. // Move content to main container
  1086. final Element itd = innermostTDinAlignmnetStructure;
  1087. final Element alignmentTable = DOM.getParent(DOM
  1088. .getParent(DOM.getParent(alignmentTD)));
  1089. final Element target = DOM.getParent(alignmentTable);
  1090. while (DOM.getChildCount(itd) > 0) {
  1091. Element content = DOM.getFirstChild(itd);
  1092. if (content != null) {
  1093. DOM.removeChild(itd, content);
  1094. DOM.appendChild(target, content);
  1095. }
  1096. }
  1097. // Remove unneeded table element
  1098. DOM.removeChild(target, alignmentTable);
  1099. alignmentTD = innermostTDinAlignmnetStructure = null;
  1100. }
  1101. }
  1102. }
  1103. /** Set class for spacing */
  1104. void setSpacingAndMargins(boolean first, boolean last) {
  1105. final Element e = getElementWrappingWidgetAndCaption();
  1106. int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
  1107. if (orientationMode == ORIENTATION_HORIZONTAL) {
  1108. if (first) {
  1109. if (margins.hasLeft()) {
  1110. paddingLeft = marginLeft;
  1111. }
  1112. } else if (hasComponentSpacing) {
  1113. paddingLeft = hSpacing;
  1114. }
  1115. if (last) {
  1116. if (margins.hasRight()) {
  1117. paddingRight = marginRight;
  1118. }
  1119. }
  1120. if (margins.hasTop()) {
  1121. paddingTop = marginTop;
  1122. }
  1123. if (margins.hasBottom()) {
  1124. paddingBottom = marginBottom;
  1125. }
  1126. } else {
  1127. if (margins.hasLeft()) {
  1128. paddingLeft = marginLeft;
  1129. }
  1130. if (margins.hasRight()) {
  1131. paddingRight = marginRight;
  1132. }
  1133. if (first) {
  1134. if (margins.hasTop()) {
  1135. paddingTop = marginTop;
  1136. }
  1137. } else if (hasComponentSpacing) {
  1138. paddingTop = vSpacing;
  1139. }
  1140. if (last && margins.hasBottom()) {
  1141. paddingBottom = marginBottom;
  1142. }
  1143. }
  1144. horizontalPadding = paddingLeft + paddingRight;
  1145. verticalPadding = paddingTop + paddingBottom;
  1146. DOM.setStyleAttribute(e, "paddingLeft", paddingLeft + "px");
  1147. DOM.setStyleAttribute(e, "paddingRight", paddingRight + "px");
  1148. DOM.setStyleAttribute(e, "paddingTop", paddingTop + "px");
  1149. DOM.setStyleAttribute(e, "paddingBottom", paddingBottom + "px");
  1150. }
  1151. public int getAllocatedHeight() {
  1152. if (lastForcedPixelHeight == -1) {
  1153. if (height == null) {
  1154. /*
  1155. * We have no height specified so return the space allocated
  1156. * by components so far
  1157. */
  1158. return getElementWrappingClipperDiv().getOffsetHeight()
  1159. - horizontalPadding;
  1160. }
  1161. return -1;
  1162. }
  1163. int available = lastForcedPixelHeight;
  1164. // Must remove caption height to report correct size to child
  1165. if (caption != null) {
  1166. available -= caption.getOffsetHeight();
  1167. }
  1168. return available;
  1169. }
  1170. public int getAllocatedWidth() {
  1171. if (width == null) {
  1172. /*
  1173. * We have no width specified so return the space allocated by
  1174. * components so far
  1175. */
  1176. return getElementWrappingClipperDiv().getOffsetWidth()
  1177. - horizontalPadding;
  1178. }
  1179. return lastForcedPixelWidth;
  1180. }
  1181. }
  1182. /* documented at super */
  1183. public void add(Widget child) {
  1184. add(child, childWidgets.size());
  1185. }
  1186. /**
  1187. * Add widget to this layout at given position.
  1188. *
  1189. * This methods supports reinserting exiting child into layout - it just
  1190. * moves the position of the child in the layout.
  1191. */
  1192. public void add(Widget child, int atIndex) {
  1193. /*
  1194. * <b>Validate:</b> Perform any sanity checks to ensure the Panel can
  1195. * accept a new Widget. Examples: checking for a valid index on
  1196. * insertion; checking that the Panel is not full if there is a max
  1197. * capacity.
  1198. */
  1199. if (atIndex < 0 || atIndex > childWidgets.size()) {
  1200. return;
  1201. }
  1202. /*
  1203. * <b>Adjust for Reinsertion:</b> Some Panels need to handle the case
  1204. * where the Widget is already a child of this Panel. Example: when
  1205. * performing a reinsert, the index might need to be adjusted to account
  1206. * for the Widget's removal. See {@link ComplexPanel#adjustIndex(Widget,
  1207. * int)}.
  1208. */
  1209. if (childWidgets.contains(child)) {
  1210. if (childWidgets.indexOf(child) == atIndex) {
  1211. return;
  1212. }
  1213. final int removeFromIndex = childWidgets.indexOf(child);
  1214. final WidgetWrapper wrapper = childWidgetWrappers
  1215. .get(removeFromIndex);
  1216. Element wrapperElement = wrapper.getElement();
  1217. final int nonWidgetChildElements = DOM
  1218. .getChildCount(wrappedChildContainer)
  1219. - childWidgets.size();
  1220. DOM.removeChild(wrappedChildContainer, wrapperElement);
  1221. DOM.insertChild(wrappedChildContainer, wrapperElement, atIndex
  1222. + nonWidgetChildElements);
  1223. childWidgets.remove(removeFromIndex);
  1224. childWidgetWrappers.remove(removeFromIndex);
  1225. childWidgets.insertElementAt(child, atIndex);
  1226. childWidgetWrappers.insertElementAt(wrapper, atIndex);
  1227. return;
  1228. }
  1229. /*
  1230. * <b>Detach Child:</b> Remove the Widget from its existing parent, if
  1231. * any. Most Panels will simply call {@link Widget#removeFromParent()}
  1232. * on the Widget.
  1233. */
  1234. child.removeFromParent();
  1235. /*
  1236. * <b>Logical Attach:</b> Any state variables of the Panel should be
  1237. * updated to reflect the addition of the new Widget. Example: the
  1238. * Widget is added to the Panel's {@link WidgetCollection} at the
  1239. * appropriate index.
  1240. */
  1241. childWidgets.insertElementAt(child, atIndex);
  1242. /*
  1243. * <b>Physical Attach:</b> The Widget's Element must be physically
  1244. * attached to the Panel's Element, either directly or indirectly.
  1245. */
  1246. final WidgetWrapper wrapper = new WidgetWrapper();
  1247. final int nonWidgetChildElements = DOM
  1248. .getChildCount(wrappedChildContainer)
  1249. - childWidgetWrappers.size();
  1250. childWidgetWrappers.insertElementAt(wrapper, atIndex);
  1251. DOM.insertChild(wrappedChildContainer, wrapper.getElement(), atIndex
  1252. + nonWidgetChildElements);
  1253. DOM.appendChild(wrapper.getElementWrappingWidgetAndCaption(), child
  1254. .getElement());
  1255. /*
  1256. * <b>Adopt:</b> Call {@link #adopt(Widget)} to finalize the add as the
  1257. * very last step.
  1258. */
  1259. adopt(child);
  1260. }
  1261. /* documented at super */
  1262. public boolean remove(Widget child) {
  1263. /*
  1264. * <b>Validate:</b> Make sure this Panel is actually the parent of the
  1265. * child Widget; return <code>false</code> if it is not.
  1266. */
  1267. if (!childWidgets.contains(child)) {
  1268. return false;
  1269. }
  1270. /*
  1271. * <b>Orphan:</b> Call {@link #orphan(Widget)} first while the child
  1272. * Widget is still attached.
  1273. */
  1274. orphan(child);
  1275. /*
  1276. * <b>Physical Detach:</b> Adjust the DOM to account for the removal of
  1277. * the child Widget. The Widget's Element must be physically removed
  1278. * from the DOM.
  1279. */
  1280. final int index = childWidgets.indexOf(child);
  1281. final WidgetWrapper wrapper = childWidgetWrappers.get(index);
  1282. DOM.removeChild(wrappedChildContainer, wrapper.getElement());
  1283. childWidgetWrappers.remove(index);
  1284. /*
  1285. * <b>Logical Detach:</b> Update the Panel's state variables to reflect
  1286. * the removal of the child Widget. Example: the Widget is removed from
  1287. * the Panel's {@link WidgetCollection}.
  1288. */
  1289. childWidgets.remove(index);
  1290. if (child instanceof Paintable) {
  1291. client.unregisterPaintable((Paintable) child);
  1292. }
  1293. return true;
  1294. }
  1295. /* documented at super */
  1296. public boolean hasChildComponent(Widget component) {
  1297. return childWidgets.contains(component);
  1298. }
  1299. /* documented at super */
  1300. public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
  1301. final int index = childWidgets.indexOf(oldComponent);
  1302. if (index >= 0) {
  1303. client.unregisterPaintable((Paintable) oldComponent);
  1304. remove(oldComponent);
  1305. add(newComponent, index);
  1306. }
  1307. }
  1308. /* documented at super */
  1309. public void updateCaption(Paintable component, UIDL uidl) {
  1310. final int index = childWidgets.indexOf(component);
  1311. if (index >= 0) {
  1312. childWidgetWrappers.get(index).updateCaption(uidl, component);
  1313. }
  1314. }
  1315. /* documented at super */
  1316. public Iterator iterator() {
  1317. return childWidgets.iterator();
  1318. }
  1319. /* documented at super */
  1320. public void iLayout(int availableWidth, int availableHeight) {
  1321. updateChildSizes(availableWidth, availableHeight);
  1322. Util.runDescendentsLayout(this);
  1323. childLayoutsHaveChanged = false;
  1324. }
  1325. public int getAllocatedHeight(Widget child) {
  1326. final int index = childWidgets.indexOf(child);
  1327. if (index >= 0) {
  1328. WidgetWrapper wrapper = childWidgetWrappers.get(index);
  1329. return wrapper.getAllocatedHeight();
  1330. }
  1331. return -1;
  1332. }
  1333. public int getAllocatedWidth(Widget child) {
  1334. final int index = childWidgets.indexOf(child);
  1335. if (index >= 0) {
  1336. WidgetWrapper wrapper = childWidgetWrappers.get(index);
  1337. return wrapper.getAllocatedWidth();
  1338. }
  1339. return -1;
  1340. }
  1341. public boolean childComponentSizesUpdated() {
  1342. if (height != null && width != null) {
  1343. /*
  1344. * If the height and width has been specified for this layout the
  1345. * child components cannot make the size of the layout change
  1346. */
  1347. return true;
  1348. }
  1349. int currentHeight = getElement().getOffsetHeight();
  1350. int currentWidth = getElement().getOffsetWidth();
  1351. if (currentHeight != renderedHeight || currentWidth != renderedWidth) {
  1352. /*
  1353. * Size has changed so we let the child components know about the
  1354. * new size.
  1355. */
  1356. iLayout(-1, -1);
  1357. return false;
  1358. } else {
  1359. /*
  1360. * Size has not changed so we do not need to propagate the event
  1361. * further
  1362. */
  1363. return true;
  1364. }
  1365. }
  1366. }