001: /*
002: * This file is part of the Echo Web Application Framework (hereinafter "Echo").
003: * Copyright (C) 2002-2005 NextApp, Inc.
004: *
005: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
006: *
007: * The contents of this file are subject to the Mozilla Public License Version
008: * 1.1 (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: * http://www.mozilla.org/MPL/
011: *
012: * Software distributed under the License is distributed on an "AS IS" basis,
013: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
014: * for the specific language governing rights and limitations under the
015: * License.
016: *
017: * Alternatively, the contents of this file may be used under the terms of
018: * either the GNU General Public License Version 2 or later (the "GPL"), or
019: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
020: * in which case the provisions of the GPL or the LGPL are applicable instead
021: * of those above. If you wish to allow use of your version of this file only
022: * under the terms of either the GPL or the LGPL, and not to allow others to
023: * use your version of this file under the terms of the MPL, indicate your
024: * decision by deleting the provisions above and replace them with the notice
025: * and other provisions required by the GPL or the LGPL. If you do not delete
026: * the provisions above, a recipient may use your version of this file under
027: * the terms of any one of the MPL, the GPL or the LGPL.
028: */
029:
030: package nextapp.echo2.webcontainer.syncpeer;
031:
032: import nextapp.echo2.app.Extent;
033: import nextapp.echo2.webcontainer.RenderContext;
034: import nextapp.echo2.webcontainer.propertyrender.ExtentRender;
035: import nextapp.echo2.webrender.Service;
036: import nextapp.echo2.webrender.WebRenderServlet;
037: import nextapp.echo2.webrender.service.StaticBinaryService;
038:
039: import org.w3c.dom.Document;
040: import org.w3c.dom.Element;
041:
042: /**
043: * Renders an HTML table that has two or three "container" cells and
044: * independently settable margins between them. These tables are useful for
045: * rendering buttons that have two or three elements (images, text labels, and
046: * state indicators). This class supports all possible permutations for
047: * placement of each of the two or three contained components.
048: * <p>
049: * This class should not be extended or used by classes outside of the
050: * Echo framework.
051: */
052: class TriCellTable {
053:
054: private static final Service TRANSPARENT_SPACER_IMAGE_SERVICE = StaticBinaryService
055: .forResource("Echo.TriCellTable.TransparentImage",
056: "image/gif",
057: "/nextapp/echo2/webcontainer/resource/image/Transparent.gif");
058:
059: static {
060: WebRenderServlet.getServiceRegistry().add(
061: TRANSPARENT_SPACER_IMAGE_SERVICE);
062: }
063:
064: static final int INVERTED = 1;
065: static final int VERTICAL = 2;
066:
067: public static final int LEADING_TRAILING = 0;
068: public static final int TRAILING_LEADING = INVERTED;
069: public static final int TOP_BOTTOM = VERTICAL;
070: public static final int BOTTOM_TOP = INVERTED | VERTICAL;
071:
072: private Element[] tdElements;
073: private Element[] marginTdElements = null;
074: private Element tableElement;
075: private Element tbodyElement;
076: private Document document;
077: private RenderContext rc;
078:
079: /**
080: * This constructor is called by the non-private constructors to set up
081: * common properties.
082: *
083: * @param rc the relevant <code>RenderContext</code>
084: * @param id The id the id upon which the inner elements will be based.
085: * the id of the returned table element is not set and must be
086: * set appropriately by the caller.
087: */
088: private TriCellTable(RenderContext rc, Document document, String id) {
089: super ();
090: this .rc = rc;
091: this .document = document;
092: tableElement = document.createElement("table");
093: tbodyElement = document.createElement("tbody");
094: tbodyElement.setAttribute("id", id + "_tbody");
095: tableElement.appendChild(tbodyElement);
096: }
097:
098: /**
099: * Creates a two-celled <code>TriCellTable</code>.
100: *
101: * @param rc the relevant <code>RenderContext</code>
102: * @param document the outgoing XML document
103: * @param id the id of the root element
104: * @param orientation0_1 The orientation of Element 0 with respect to
105: * Element 1, one of the following values:
106: * <ul>
107: * <li>LEFT_RIGHT (element 0 is to the left of element 1)</li>
108: * <li>RIGHT_LEFT (element 1 is to the left of element 0)</li>
109: * <li>TOP_BOTTOM (element 0 is above element 1)</li>
110: * <li>BOTTOM_TOP (element 1 is above element 0)</li>
111: * </ul>
112: * @param margin0_1 The margin size between element 0 and element 1.
113: */
114: TriCellTable(RenderContext rc, Document document, String id,
115: int orientation0_1, Extent margin0_1) {
116: this (rc, document, id);
117:
118: marginTdElements = new Element[1];
119: tdElements = new Element[2];
120: tdElements[0] = document.createElement("td");
121: tdElements[0].setAttribute("id", id + "_td_0");
122: tdElements[1] = document.createElement("td");
123: tdElements[1].setAttribute("id", id + "_td_1");
124:
125: if (margin0_1 != null && margin0_1.getValue() > 0) {
126: marginTdElements[0] = document.createElement("td");
127: marginTdElements[0]
128: .setAttribute("id", id + "_tdmargin_0_1");
129: int size = ExtentRender.toPixels(margin0_1, 1);
130: if ((orientation0_1 & VERTICAL) == 0) {
131: marginTdElements[0].setAttribute("style", "width:"
132: + size + "px;");
133: addSpacer(marginTdElements[0], size, false);
134: } else {
135: marginTdElements[0].setAttribute("style", "height:"
136: + size + "px;");
137: addSpacer(marginTdElements[0], size, true);
138: }
139: }
140:
141: if ((orientation0_1 & VERTICAL) == 0) {
142: // horizontally oriented
143: Element trElement = document.createElement("tr");
144: trElement.setAttribute("id", id + "_tr_0_1");
145: if ((orientation0_1 & INVERTED) == 0) {
146: // normal (left to right)
147: addColumn(trElement, tdElements[0]);
148: addColumn(trElement, marginTdElements[0]);
149: addColumn(trElement, tdElements[1]);
150: } else {
151: // inverted (right to left)
152: addColumn(trElement, tdElements[1]);
153: addColumn(trElement, marginTdElements[0]);
154: addColumn(trElement, tdElements[0]);
155: }
156: tbodyElement.appendChild(trElement);
157: } else {
158: // vertically oriented
159: if ((orientation0_1 & INVERTED) == 0) {
160: // normal (top to bottom)
161: addRow(tdElements[0], id + "_tr_0");
162: addRow(marginTdElements[0], id + "_trmargin_0_1");
163: addRow(tdElements[1], id + "_tr_1");
164: } else {
165: // inverted (bottom to top)
166: addRow(tdElements[1], id + "_tr_1");
167: addRow(marginTdElements[0], id + "_trmargin_0_1");
168: addRow(tdElements[0], id + "_tr_0");
169: }
170: }
171: }
172:
173: /**
174: * Creates a three-celled <code>TriCellTable</code>.
175: *
176: * @param rc the relevant <code>RenderContext</code>
177: * @param document the outgoing XML document
178: * @param id the id of the root element
179: * @param orientation0_1 The orientation of Element 0 with respect to
180: * Element 1, one of the following values:
181: * <ul>
182: * <li>LEFT_RIGHT (element 0 is to the left of element 1)</li>
183: * <li>RIGHT_LEFT (element 1 is to the left of element 0)</li>
184: * <li>TOP_BOTTOM (element 0 is above element 1)</li>
185: * <li>BOTTOM_TOP (element 1 is above element 0)</li>
186: * </ul>
187: * @param margin0_1 The margin size between element 0 and element 1.
188: * @param orientation01_2 The orientation of Elements 0 and 1 with
189: * respect to Element 2, one of the following values:
190: * <ul>
191: * <li>LEFT_RIGHT (elements 0 and 1 are to the left of
192: * element 1)</li>
193: * <li>RIGHT_LEFT (element 2 is to the left of elements 0 and 1)</li>
194: * <li>TOP_BOTTOM (elements 0 and 1 are above element 2)</li>
195: * <li>BOTTOM_TOP (element 2 is above elements 0 and 1)</li>
196: * </ul>
197: * @param margin01_2 The margin size between the combination
198: * of elements 0 and 1 and element 2.
199: */
200: TriCellTable(RenderContext rc, Document document, String id,
201: int orientation0_1, Extent margin0_1, int orientation01_2,
202: Extent margin01_2) {
203: this (rc, document, id);
204:
205: Element trElement;
206:
207: marginTdElements = new Element[2];
208: tdElements = new Element[3];
209: tdElements[0] = document.createElement("td");
210: tdElements[0].setAttribute("id", id + "_td_0");
211: tdElements[1] = document.createElement("td");
212: tdElements[1].setAttribute("id", id + "_td_1");
213: tdElements[2] = document.createElement("td");
214: tdElements[2].setAttribute("id", id + "_td_2");
215:
216: // Create margin cells
217: if (margin0_1 != null || margin01_2 != null) {
218: if (margin0_1 != null && margin0_1.getValue() > 0) {
219: marginTdElements[0] = document.createElement("td");
220: marginTdElements[0].setAttribute("id", id
221: + "_tdmargin_0_1");
222:
223: int size = ExtentRender.toPixels(margin0_1, 1);
224: if ((orientation0_1 & VERTICAL) == 0) {
225: marginTdElements[0].setAttribute("style", "width:"
226: + size + "px;");
227: addSpacer(marginTdElements[0], size, false);
228: } else {
229: marginTdElements[0].setAttribute("style", "height:"
230: + size + "px;");
231: addSpacer(marginTdElements[0], size, true);
232: }
233: }
234: if (margin01_2 != null && margin01_2.getValue() > 0) {
235: marginTdElements[1] = document.createElement("td");
236: marginTdElements[1].setAttribute("id", id
237: + "_tdmargin_01_2");
238:
239: int size = ExtentRender.toPixels(margin01_2, 1);
240: if ((orientation01_2 & VERTICAL) == 0) {
241: marginTdElements[1].setAttribute("style", "width:"
242: + size + "px;");
243: addSpacer(marginTdElements[1], size, false);
244: } else {
245: marginTdElements[1].setAttribute("style", "height:"
246: + size + "px;");
247: addSpacer(marginTdElements[1], size, true);
248: }
249: }
250: }
251:
252: if ((orientation0_1 & VERTICAL) == 0) {
253: // horizontally oriented 0/1
254: if ((orientation01_2 & VERTICAL) == 0) {
255: // horizontally oriented 01/2
256: trElement = document.createElement("tr");
257: trElement.setAttribute("id", id + "_tr_0");
258: if ((orientation01_2 & INVERTED) != 0) {
259: // 2 before 01: render #2 and margin at beginning of TR.
260: addColumn(trElement, tdElements[2]);
261: addColumn(trElement, marginTdElements[1]);
262: }
263:
264: // Render 01
265: if ((orientation0_1 & INVERTED) == 0) {
266: // normal (left to right)
267: addColumn(trElement, tdElements[0]);
268: addColumn(trElement, marginTdElements[0]);
269: addColumn(trElement, tdElements[1]);
270: } else {
271: // inverted (right to left)
272: addColumn(trElement, tdElements[1]);
273: addColumn(trElement, marginTdElements[0]);
274: addColumn(trElement, tdElements[0]);
275: }
276:
277: if ((orientation01_2 & INVERTED) == 0) {
278: addColumn(trElement, marginTdElements[1]);
279: addColumn(trElement, tdElements[2]);
280: }
281:
282: tbodyElement.appendChild(trElement);
283: } else {
284: // vertically oriented 01/2
285:
286: // determine and apply column span based on presence of margin between 0 and 1
287: int columns = (margin0_1 != null && margin0_1
288: .getValue() > 0) ? 3 : 2;
289: tdElements[2].setAttribute("colspan", Integer
290: .toString(columns));
291: if (marginTdElements[1] != null) {
292: marginTdElements[1].setAttribute("colspan", Integer
293: .toString(columns));
294: }
295:
296: if ((orientation01_2 & INVERTED) != 0) {
297: // 2 before 01: render #2 and margin at beginning of TR.
298: addRow(tdElements[2], id + "_tr_2");
299: addRow(marginTdElements[1], id + "_trmargin_01_2");
300: }
301:
302: // Render 01
303: trElement = document.createElement("tr");
304: trElement.setAttribute("id", "tr_" + id);
305: if ((orientation0_1 & INVERTED) == 0) {
306: // normal (left to right)
307: addColumn(trElement, tdElements[0]);
308: addColumn(trElement, marginTdElements[0]);
309: addColumn(trElement, tdElements[1]);
310: } else {
311: // inverted (right to left)
312: addColumn(trElement, tdElements[1]);
313: addColumn(trElement, marginTdElements[0]);
314: addColumn(trElement, tdElements[0]);
315: }
316: tbodyElement.appendChild(trElement);
317:
318: if ((orientation01_2 & INVERTED) == 0) {
319: // 01 before 2: render margin and #2 at end of TR.
320: addRow(marginTdElements[1], id + "_trmargin_01_2");
321: addRow(tdElements[2], id + "_tr_2");
322: }
323: }
324: } else {
325: // vertically oriented 0/1
326: if ((orientation01_2 & VERTICAL) == 0) {
327: // horizontally oriented 01/2
328:
329: // determine and apply row span based on presence of margin between 0 and 1
330: int rows = (margin0_1 != null && margin0_1.getValue() > 0) ? 3
331: : 2;
332: tdElements[2].setAttribute("rowspan", Integer
333: .toString(rows));
334: if (marginTdElements[1] != null) {
335: marginTdElements[1].setAttribute("rowspan", Integer
336: .toString(rows));
337: }
338:
339: trElement = document.createElement("tr");
340: trElement.setAttribute("id", id + "_tr_0");
341: if ((orientation01_2 & INVERTED) != 0) {
342: addColumn(trElement, tdElements[2]);
343: addColumn(trElement, marginTdElements[1]);
344: if ((orientation0_1 & INVERTED) == 0) {
345: addColumn(trElement, tdElements[0]);
346: } else {
347: addColumn(trElement, tdElements[1]);
348: }
349: } else {
350: if ((orientation0_1 & INVERTED) == 0) {
351: addColumn(trElement, tdElements[0]);
352: } else {
353: addColumn(trElement, tdElements[1]);
354: }
355: addColumn(trElement, marginTdElements[1]);
356: addColumn(trElement, tdElements[2]);
357: }
358: tbodyElement.appendChild(trElement);
359: addRow(marginTdElements[0], id + "_trmargin_0_1");
360: if ((orientation0_1 & INVERTED) == 0) {
361: addRow(tdElements[1], id + "_tr_1");
362: } else {
363: addRow(tdElements[0], id + "_tr_0");
364: }
365: } else {
366: // vertically oriented 01/2
367: if ((orientation01_2 & INVERTED) != 0) {
368: // 2 before 01: render #2 and margin at beginning of TABLE.
369: addRow(tdElements[2], id + "_tr_2");
370: addRow(marginTdElements[1], id + "_trmargin_01_2");
371: }
372:
373: // Render 01
374: if ((orientation0_1 & INVERTED) == 0) {
375: // normal (top to bottom)
376: addRow(tdElements[0], id + "_tr_0");
377: addRow(marginTdElements[0], id + "_trmargin_0_1");
378: addRow(tdElements[1], id + "_tr_1");
379: } else {
380: // inverted (bottom to top)
381: addRow(tdElements[1], id + "_tr_1");
382: addRow(marginTdElements[0], id + "_trmargin_1_0");
383: addRow(tdElements[0], id + "_tr_0");
384: }
385:
386: if ((orientation01_2 & INVERTED) == 0) {
387: // 01 before 2: render margin and #2 at end of TABLE.
388: addRow(marginTdElements[1], id + "_trmargin_01_2");
389: addRow(tdElements[2], id + "_tr_2");
390: }
391: }
392: }
393: }
394:
395: /**
396: * Adds an cell element to a table row. The element will not be added
397: * if null is provided for the value of <code>td</code>.
398: *
399: * @param tr The table row to which the cell element is to be added.
400: * @param td The cell element to be added (if not null).
401: */
402: private void addColumn(Element tr, Element td) {
403: if (td != null) {
404: tr.appendChild(td);
405: }
406: }
407:
408: /**
409: * Appends CSS text to the 'style' attribute of each table cell 'td'
410: * element.
411: *
412: * @param cssText the CSS text to add
413: */
414: void addCellCssText(String cssText) {
415: for (int i = 0; i < tdElements.length; ++i) {
416: if (tdElements[i].hasAttribute("style")) {
417: tdElements[i].setAttribute("style", tdElements[i]
418: .getAttribute("style")
419: + cssText);
420: } else {
421: tdElements[i].setAttribute("style", cssText);
422: }
423: }
424: if (marginTdElements != null) {
425: for (int i = 0; i < marginTdElements.length; ++i) {
426: if (marginTdElements[i] != null) {
427: if (marginTdElements[i].hasAttribute("style")) {
428: marginTdElements[i].setAttribute("style",
429: marginTdElements[i]
430: .getAttribute("style")
431: + cssText);
432: } else {
433: marginTdElements[i].setAttribute("style",
434: cssText);
435: }
436: }
437: }
438: }
439: }
440:
441: /**
442: * Adds a row containing a single column element to a table.
443: * The row will not be added if <code>td</code> is null.
444: *
445: * @param tdElement The row element to be added.
446: * @param trElementId the id to assign to the row element.
447: */
448: private void addRow(Element tdElement, String trElementId) {
449: if (tdElement != null) {
450: Element trElement = document.createElement("tr");
451: trElement.setAttribute("id", trElementId);
452: trElement.appendChild(tdElement);
453: tbodyElement.appendChild(trElement);
454: }
455: }
456:
457: /**
458: * Adds a spacer element containing a transparent GIF image.
459: * These are unfortunately necessary to prevent spacer cells
460: * from collapsing in circumstances where horizontal real-estate
461: * is not available.
462: *
463: * @param parentElement the <code>Element</code> to which the spacer image
464: * is to be appended
465: * @param size the size of the spacer cell, in pixels
466: * @param vertical a flag indicating the direction; specify
467: * <code>true</code> for a vertical spacer or <code>false</code> for
468: * a horizontal spacer.
469: */
470: private void addSpacer(Element parentElement, int size,
471: boolean vertical) {
472: Element imgElement = document.createElement("img");
473: imgElement.setAttribute("src", rc.getContainerInstance()
474: .getServiceUri(TRANSPARENT_SPACER_IMAGE_SERVICE));
475: imgElement.setAttribute("alt", "");
476: imgElement.setAttribute("width", vertical ? "1" : Integer
477: .toString(size));
478: imgElement.setAttribute("height", vertical ? Integer
479: .toString(size) : "1");
480: parentElement.appendChild(imgElement);
481: }
482:
483: /**
484: * Returns the created table element.
485: *
486: * @return the table element
487: */
488: Element getTableElement() {
489: return tableElement;
490: }
491:
492: /**
493: * Returns the specified container element.
494: *
495: * @param index The index of the table element to return. For two-celled tables,
496: * legitimate values are 0 and 1. For three-celled tables,
497: * legitimate values are 0, 1, and 2.
498: * @return The specified container element.
499: */
500: Element getTdElement(int index) {
501: return tdElements[index];
502: }
503:
504: /**
505: * Returns the specified margin element.
506: *
507: * @param index The index of the table element to return. Index 0 is the margin
508: * element between container cells 0 and 1. Index 1 is the margin
509: * element between container cells 0/1 and 2.
510: * @return The specified margin element. Returns null if the margin is zero
511: * pixels.
512: */
513: Element getMarginTdElement(int index) {
514: return marginTdElements[index];
515: }
516: }
|