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.FillImage;
033: import nextapp.echo2.app.Color;
034: import nextapp.echo2.app.Component;
035: import nextapp.echo2.app.Extent;
036: import nextapp.echo2.app.Font;
037: import nextapp.echo2.app.ImageReference;
038: import nextapp.echo2.app.LayoutData;
039: import nextapp.echo2.app.LayoutDirection;
040: import nextapp.echo2.app.Pane;
041: import nextapp.echo2.app.SplitPane;
042: import nextapp.echo2.app.layout.SplitPaneLayoutData;
043: import nextapp.echo2.app.update.ServerComponentUpdate;
044: import nextapp.echo2.webcontainer.ContainerInstance;
045: import nextapp.echo2.webcontainer.PartialUpdateManager;
046: import nextapp.echo2.webcontainer.PartialUpdateParticipant;
047: import nextapp.echo2.webcontainer.PropertyUpdateProcessor;
048: import nextapp.echo2.webcontainer.RenderContext;
049: import nextapp.echo2.webcontainer.ComponentSynchronizePeer;
050: import nextapp.echo2.webcontainer.RenderState;
051: import nextapp.echo2.webcontainer.SynchronizePeerFactory;
052: import nextapp.echo2.webcontainer.image.ImageRenderSupport;
053: import nextapp.echo2.webcontainer.propertyrender.AlignmentRender;
054: import nextapp.echo2.webcontainer.propertyrender.ColorRender;
055: import nextapp.echo2.webcontainer.propertyrender.ExtentRender;
056: import nextapp.echo2.webcontainer.propertyrender.FillImageRender;
057: import nextapp.echo2.webcontainer.propertyrender.FontRender;
058: import nextapp.echo2.webcontainer.propertyrender.InsetsRender;
059: import nextapp.echo2.webrender.ServerMessage;
060: import nextapp.echo2.webrender.Service;
061: import nextapp.echo2.webrender.WebRenderServlet;
062: import nextapp.echo2.webrender.output.CssStyle;
063: import nextapp.echo2.webrender.servermessage.DomUpdate;
064: import nextapp.echo2.webrender.service.JavaScriptService;
065:
066: import org.w3c.dom.Element;
067:
068: /**
069: * Synchronization peer for <code>nextapp.echo2.app.SplitPane</code> components.
070: * <p>
071: * This class should not be extended or used by classes outside of the
072: * Echo framework.
073: */
074: public class SplitPanePeer implements ImageRenderSupport,
075: PropertyUpdateProcessor, ComponentSynchronizePeer {
076:
077: //TODO: Performance can be improved by implementing PartialUpdateManagers.
078:
079: private static final String IMAGE_ID_HORIZONTAL_SEPARATOR = "horizontalSeparator";
080: private static final String IMAGE_ID_PANE_0_BACKGROUND = "pane0Background";
081: private static final String IMAGE_ID_PANE_1_BACKGROUND = "pane1Background";
082: private static final String IMAGE_ID_VERTICAL_SEPARATOR = "verticalSeparator";
083:
084: private static final Extent DEFAULT_SEPARATOR_POSITION = new Extent(
085: 100);
086:
087: /**
088: * <code>RenderState</code> implementation.
089: */
090: static class RenderStateImpl implements RenderState {
091:
092: /**
093: * The pane which was rendered at index 0.
094: */
095: private String pane0;
096:
097: /**
098: * The pane which was rendered at index 1.
099: */
100: private String pane1;
101:
102: /**
103: * Creates a new <code>RenderState</code> based on the state of the
104: * given <code>splitPane</code>.
105: *
106: * @param splitPane the split pane
107: */
108: private RenderStateImpl(SplitPane splitPane) {
109: int componentCount = splitPane.getVisibleComponentCount();
110: pane0 = (componentCount < 1 || splitPane
111: .getVisibleComponent(0) == null) ? null
112: : ContainerInstance.getElementId(splitPane
113: .getVisibleComponent(0));
114: pane1 = (componentCount < 2 || splitPane
115: .getVisibleComponent(1) == null) ? null
116: : ContainerInstance.getElementId(splitPane
117: .getVisibleComponent(1));
118: }
119: }
120:
121: /**
122: * Service to provide supporting JavaScript library.
123: */
124: private static final Service SPLIT_PANE_SERVICE = JavaScriptService
125: .forResource("Echo.SplitPane",
126: "/nextapp/echo2/webcontainer/resource/js/SplitPane.js");
127:
128: static {
129: WebRenderServlet.getServiceRegistry().add(SPLIT_PANE_SERVICE);
130: }
131:
132: /**
133: * Utility method to evaluate equality of objects in a null-safe fashion.
134: */
135: private static final boolean equal(Object a, Object b) {
136: return a == b || (a != null && a.equals(b));
137: }
138:
139: /**
140: * <code>PartialUpdateParticipant</code> to update position of separator.
141: */
142: private PartialUpdateParticipant separatorPositionUpdate = new PartialUpdateParticipant() {
143:
144: /**
145: * @see nextapp.echo2.webcontainer.PartialUpdateParticipant#renderProperty(nextapp.echo2.webcontainer.RenderContext, nextapp.echo2.app.update.ServerComponentUpdate)
146: */
147: public void renderProperty(RenderContext rc,
148: ServerComponentUpdate update) {
149: SplitPane splitPane = (SplitPane) update.getParent();
150: renderSetSeparatorPositionDirective(rc, splitPane);
151: }
152:
153: /**
154: * @see nextapp.echo2.webcontainer.PartialUpdateParticipant#canRenderProperty(nextapp.echo2.webcontainer.RenderContext, nextapp.echo2.app.update.ServerComponentUpdate)
155: */
156: public boolean canRenderProperty(RenderContext rc,
157: ServerComponentUpdate update) {
158: return true;
159: }
160:
161: };
162:
163: private PartialUpdateManager partialUpdateManager;
164:
165: /**
166: * Default constructor.
167: */
168: public SplitPanePeer() {
169: super ();
170: partialUpdateManager = new PartialUpdateManager();
171: partialUpdateManager.add(SplitPane.PROPERTY_SEPARATOR_POSITION,
172: separatorPositionUpdate);
173: }
174:
175: /**
176: * Calculates the pixel size of the separator.
177: *
178: * @param splitPane the <code>SplitPane</code> to evaluate
179: * @return the size of the separator in pixels
180: */
181: private int calculateSeparatorSize(SplitPane splitPane) {
182: Boolean booleanValue = (Boolean) splitPane
183: .getRenderProperty(SplitPane.PROPERTY_RESIZABLE);
184: boolean resizable = booleanValue == null ? false : booleanValue
185: .booleanValue();
186: boolean verticalOrientation = isOrientationVertical(splitPane);
187: if (resizable) {
188: return ExtentRender
189: .toPixels(
190: (Extent) splitPane
191: .getRenderProperty(verticalOrientation ? SplitPane.PROPERTY_SEPARATOR_HEIGHT
192: : SplitPane.PROPERTY_SEPARATOR_WIDTH),
193: 4);
194: } else {
195: return ExtentRender
196: .toPixels(
197: (Extent) splitPane
198: .getRenderProperty(verticalOrientation ? SplitPane.PROPERTY_SEPARATOR_HEIGHT
199: : SplitPane.PROPERTY_SEPARATOR_WIDTH),
200: 0);
201: }
202: }
203:
204: /**
205: * @see nextapp.echo2.webcontainer.ComponentSynchronizePeer#getContainerId(nextapp.echo2.app.Component)
206: */
207: public String getContainerId(Component child) {
208: return ContainerInstance.getElementId(child.getParent())
209: + "_pane" + child.getParent().visibleIndexOf(child);
210: }
211:
212: /**
213: * @see nextapp.echo2.webcontainer.image.ImageRenderSupport#getImage(nextapp.echo2.app.Component, java.lang.String)
214: */
215: public ImageReference getImage(Component component, String imageId) {
216: if (IMAGE_ID_PANE_0_BACKGROUND.equals(imageId)) {
217: return getPaneBackgroundImage(component
218: .getVisibleComponent(0));
219: } else if (IMAGE_ID_PANE_1_BACKGROUND.equals(imageId)) {
220: return getPaneBackgroundImage(component
221: .getVisibleComponent(1));
222: } else if (IMAGE_ID_HORIZONTAL_SEPARATOR.equals(imageId)) {
223: FillImage fillImage = (FillImage) component
224: .getRenderProperty(SplitPane.PROPERTY_SEPARATOR_HORIZONTAL_IMAGE);
225: return fillImage == null ? null : fillImage.getImage();
226: } else if (IMAGE_ID_VERTICAL_SEPARATOR.equals(imageId)) {
227: FillImage fillImage = (FillImage) component
228: .getRenderProperty(SplitPane.PROPERTY_SEPARATOR_VERTICAL_IMAGE);
229: return fillImage == null ? null : fillImage.getImage();
230: } else {
231: return null;
232: }
233: }
234:
235: /**
236: * Retrieves the <code>ImageReference</code> of the background image
237: * defined in the layout data of the specified component (pane).
238: * Returns null if the image cannot be retrieved for any reason.
239: *
240: * @param component the child pane component
241: * @return the background image
242: */
243: private ImageReference getPaneBackgroundImage(Component component) {
244: LayoutData layoutData = (LayoutData) component
245: .getRenderProperty(SplitPane.PROPERTY_LAYOUT_DATA);
246: //TODO. Investigate use of instanceof here.
247: if (!(layoutData instanceof SplitPaneLayoutData)) {
248: return null;
249: }
250: FillImage backgroundImage = ((SplitPaneLayoutData) layoutData)
251: .getBackgroundImage();
252: if (backgroundImage == null) {
253: return null;
254: }
255: return backgroundImage.getImage();
256: }
257:
258: private int getRenderOrientation(SplitPane splitPane) {
259: Integer orientationValue = (Integer) splitPane
260: .getRenderProperty(SplitPane.PROPERTY_ORIENTATION);
261: int orientation = orientationValue == null ? SplitPane.ORIENTATION_HORIZONTAL_LEADING_TRAILING
262: : orientationValue.intValue();
263: if (orientation == SplitPane.ORIENTATION_HORIZONTAL_LEADING_TRAILING
264: || orientation == SplitPane.ORIENTATION_HORIZONTAL_TRAILING_LEADING) {
265: LayoutDirection layoutDirection = splitPane
266: .getRenderLayoutDirection();
267: if (orientation == SplitPane.ORIENTATION_HORIZONTAL_LEADING_TRAILING) {
268: orientation = layoutDirection.isLeftToRight() ? SplitPane.ORIENTATION_HORIZONTAL_LEFT_RIGHT
269: : SplitPane.ORIENTATION_HORIZONTAL_RIGHT_LEFT;
270: } else {
271: orientation = layoutDirection.isLeftToRight() ? SplitPane.ORIENTATION_HORIZONTAL_RIGHT_LEFT
272: : SplitPane.ORIENTATION_HORIZONTAL_LEFT_RIGHT;
273: }
274: }
275: return orientation;
276: }
277:
278: /**
279: * Determines if the orientation of a <code>SplitPane</code> is horizontal
280: * or vertical.
281: *
282: * @param splitPane the <code>SplitPane</code> to analyze
283: * @return true if the orientation is vertical, false if it is horizontal
284: */
285: private boolean isOrientationVertical(SplitPane splitPane) {
286: Integer orientationValue = (Integer) splitPane
287: .getRenderProperty(SplitPane.PROPERTY_ORIENTATION);
288: int orientation = orientationValue == null ? SplitPane.ORIENTATION_HORIZONTAL
289: : orientationValue.intValue();
290: return orientation == SplitPane.ORIENTATION_VERTICAL_TOP_BOTTOM
291: || orientation == SplitPane.ORIENTATION_VERTICAL_BOTTOM_TOP;
292: }
293:
294: /**
295: * @see nextapp.echo2.webcontainer.PropertyUpdateProcessor#processPropertyUpdate(
296: * nextapp.echo2.webcontainer.ContainerInstance, nextapp.echo2.app.Component, org.w3c.dom.Element)
297: */
298: public void processPropertyUpdate(ContainerInstance ci,
299: Component component, Element propertyElement) {
300: if ("separatorPosition".equals(propertyElement
301: .getAttribute(PropertyUpdateProcessor.PROPERTY_NAME))) {
302: Extent newValue = ExtentRender
303: .toExtent(propertyElement
304: .getAttribute(PropertyUpdateProcessor.PROPERTY_VALUE));
305: ci.getUpdateManager().getClientUpdateManager()
306: .setComponentProperty(component,
307: SplitPane.PROPERTY_SEPARATOR_POSITION,
308: newValue);
309: }
310: }
311:
312: /**
313: * @see nextapp.echo2.webcontainer.ComponentSynchronizePeer#renderAdd(nextapp.echo2.webcontainer.RenderContext,
314: * nextapp.echo2.app.update.ServerComponentUpdate, java.lang.String, nextapp.echo2.app.Component)
315: */
316: public void renderAdd(RenderContext rc,
317: ServerComponentUpdate update, String targetId,
318: Component component) {
319: ServerMessage serverMessage = rc.getServerMessage();
320: serverMessage.addLibrary(SPLIT_PANE_SERVICE.getId());
321: SplitPane splitPane = (SplitPane) component;
322: renderInitDirective(rc, splitPane, targetId);
323: Component[] children = splitPane.getVisibleComponents();
324: for (int i = 0; i < children.length; ++i) {
325: renderChild(rc, update, splitPane, children[i]);
326: }
327: updateRenderState(rc, splitPane);
328: }
329:
330: private void renderAddChildDirective(RenderContext rc,
331: ServerComponentUpdate update, SplitPane splitPane, int index) {
332: String elementId = ContainerInstance.getElementId(splitPane);
333: ServerMessage serverMessage = rc.getServerMessage();
334: Element partElement = serverMessage.addPart(
335: ServerMessage.GROUP_ID_UPDATE,
336: "EchoSplitPane.MessageProcessor");
337: Element addChildElement = serverMessage.getDocument()
338: .createElement("add-child");
339: addChildElement.setAttribute("eid", elementId);
340: addChildElement.setAttribute("index", Integer.toString(index));
341: Component child = splitPane.getVisibleComponent(index);
342: renderLayoutData(rc, addChildElement, child, index);
343: partElement.appendChild(addChildElement);
344: renderChild(rc, update, splitPane, child);
345: }
346:
347: /**
348: * Renders child components which were added to a
349: * <code>SplitPane</code>, as described in the provided
350: * <code>ServerComponentUpdate</code>.
351: *
352: * @param rc the relevant <code>RenderContext</code>
353: * @param update the update
354: */
355: private void renderAddChildren(RenderContext rc,
356: ServerComponentUpdate update) {
357: ContainerInstance ci = rc.getContainerInstance();
358: SplitPane splitPane = (SplitPane) update.getParent();
359:
360: RenderStateImpl previousRenderState = (RenderStateImpl) ci
361: .getRenderState(splitPane);
362: RenderStateImpl currentRenderState = new RenderStateImpl(
363: splitPane);
364: if (!equal(previousRenderState.pane0, currentRenderState.pane0)) {
365: if (currentRenderState.pane0 != null) {
366: renderAddChildDirective(rc, update, splitPane, 0);
367: }
368: }
369: if (!equal(previousRenderState.pane1, currentRenderState.pane1)) {
370: if (currentRenderState.pane1 != null) {
371: renderAddChildDirective(rc, update, splitPane, 1);
372: }
373: }
374: }
375:
376: /**
377: * Renders an individual child component of the <code>SplitPane</code>.
378: *
379: * @param rc the relevant <code>RenderContext</code>
380: * @param update the <code>ServerComponentUpdate</code> being performed
381: * @param child The child <code>Component</code> to be rendered
382: */
383: private void renderChild(RenderContext rc,
384: ServerComponentUpdate update, SplitPane splitPane,
385: Component child) {
386: ComponentSynchronizePeer syncPeer = SynchronizePeerFactory
387: .getPeerForComponent(child.getClass());
388: syncPeer.renderAdd(rc, update, getContainerId(child), child);
389: }
390:
391: /**
392: * @see nextapp.echo2.webcontainer.ComponentSynchronizePeer#renderDispose(nextapp.echo2.webcontainer.RenderContext,
393: * nextapp.echo2.app.update.ServerComponentUpdate, nextapp.echo2.app.Component)
394: */
395: public void renderDispose(RenderContext rc,
396: ServerComponentUpdate update, Component component) {
397: ServerMessage serverMessage = rc.getServerMessage();
398: serverMessage.addLibrary(SPLIT_PANE_SERVICE.getId());
399: renderDisposeDirective(rc, (SplitPane) component);
400:
401: //BUGBUG. Temporarily removed, pending confirmation that fix is no longer relevant with new client-rendered SplitPane arch.
402: // // Performance Hack for Mozilla/Firefox Browsers:
403: // if (rc.getContainerInstance().getClientProperties()
404: // .getBoolean(ClientProperties.QUIRK_MOZILLA_PERFORMANCE_LARGE_DOM_REMOVE)) {
405: // String elementId = ContainerInstance.getElementId(component);
406: // if (!update.hasRemovedChild(component)) {
407: // DomUpdate.renderElementRemove(rc.getServerMessage(), elementId);
408: // }
409: // }
410: }
411:
412: /**
413: * Renders a directive to the outgoing <code>ServerMessage</code> to
414: * dispose the state of a <code>SplitPane</code>, performing tasks such as
415: * unregistering event listeners on the client.
416: *
417: * @param rc the relevant <code>RenderContext</code>
418: * @param splitPane the <code>SplitPane</code>
419: */
420: private void renderDisposeDirective(RenderContext rc,
421: SplitPane splitPane) {
422: String elementId = ContainerInstance.getElementId(splitPane);
423: ServerMessage serverMessage = rc.getServerMessage();
424: Element initElement = serverMessage.appendPartDirective(
425: ServerMessage.GROUP_ID_PREREMOVE,
426: "EchoSplitPane.MessageProcessor", "dispose");
427: initElement.setAttribute("eid", elementId);
428: }
429:
430: private int getSeparatorPosition(SplitPane splitPane) {
431: Extent separatorPosition = (Extent) splitPane
432: .getRenderProperty(
433: SplitPane.PROPERTY_SEPARATOR_POSITION,
434: DEFAULT_SEPARATOR_POSITION);
435: return ExtentRender.toPixels(separatorPosition, 100);
436: }
437:
438: /**
439: * Renders a directive to the outgoing <code>ServerMessage</code> to
440: * render a <code>SplitPane</code>.
441: *
442: * @param rc the relevant <code>RenderContext</code>
443: * @param splitPane the <code>SplitPane</code>
444: * @param targetId the id of the container element
445: */
446: private void renderInitDirective(RenderContext rc,
447: SplitPane splitPane, String targetId) {
448: String elementId = ContainerInstance.getElementId(splitPane);
449: boolean vertical = isOrientationVertical(splitPane);
450: ServerMessage serverMessage = rc.getServerMessage();
451: Element partElement = serverMessage.addPart(
452: ServerMessage.GROUP_ID_UPDATE,
453: "EchoSplitPane.MessageProcessor");
454: Element initElement = serverMessage.getDocument()
455: .createElement("init");
456: initElement.setAttribute("container-eid", targetId);
457: initElement.setAttribute("eid", elementId);
458:
459: initElement.setAttribute("position", Integer
460: .toString(getSeparatorPosition(splitPane)));
461:
462: int orientation = getRenderOrientation(splitPane);
463: switch (orientation) {
464: case SplitPane.ORIENTATION_HORIZONTAL_LEFT_RIGHT:
465: initElement.setAttribute("orientation", "l-r");
466: break;
467: case SplitPane.ORIENTATION_HORIZONTAL_RIGHT_LEFT:
468: initElement.setAttribute("orientation", "r-l");
469: break;
470: case SplitPane.ORIENTATION_VERTICAL_TOP_BOTTOM:
471: initElement.setAttribute("orientation", "t-b");
472: break;
473: case SplitPane.ORIENTATION_VERTICAL_BOTTOM_TOP:
474: initElement.setAttribute("orientation", "b-t");
475: break;
476: default:
477: throw new IllegalStateException("Invalid orientation: "
478: + orientation);
479: }
480:
481: if (!splitPane.isRenderEnabled()) {
482: initElement.setAttribute("enabled", "false");
483: }
484: Color background = (Color) splitPane
485: .getRenderProperty(SplitPane.PROPERTY_BACKGROUND);
486: if (background != null) {
487: initElement.setAttribute("background", ColorRender
488: .renderCssAttributeValue(background));
489: }
490: Color foreground = (Color) splitPane
491: .getRenderProperty(SplitPane.PROPERTY_FOREGROUND);
492: if (foreground != null) {
493: initElement.setAttribute("foreground", ColorRender
494: .renderCssAttributeValue(foreground));
495: }
496: Font font = (Font) splitPane
497: .getRenderProperty(SplitPane.PROPERTY_FONT);
498: if (font != null) {
499: CssStyle fontCssStyle = new CssStyle();
500: FontRender.renderToStyle(fontCssStyle, font);
501: initElement.setAttribute("font", fontCssStyle
502: .renderInline());
503: }
504:
505: Boolean booleanValue = (Boolean) splitPane
506: .getRenderProperty(SplitPane.PROPERTY_RESIZABLE);
507: boolean resizable = booleanValue == null ? false : booleanValue
508: .booleanValue();
509: initElement.setAttribute("resizable", resizable ? "true"
510: : "false");
511:
512: initElement.setAttribute("separator-size", Integer
513: .toString(calculateSeparatorSize(splitPane)));
514:
515: Color separatorColor = (Color) splitPane
516: .getRenderProperty(SplitPane.PROPERTY_SEPARATOR_COLOR);
517: if (separatorColor != null) {
518: initElement.setAttribute("separator-color", ColorRender
519: .renderCssAttributeValue(separatorColor));
520: }
521:
522: FillImage separatorImage = vertical ? (FillImage) splitPane
523: .getRenderProperty(SplitPane.PROPERTY_SEPARATOR_VERTICAL_IMAGE)
524: : (FillImage) splitPane
525: .getRenderProperty(SplitPane.PROPERTY_SEPARATOR_HORIZONTAL_IMAGE);
526: if (separatorImage != null) {
527: CssStyle fillImageCssStyle = new CssStyle();
528: FillImageRender.renderToStyle(fillImageCssStyle, rc, this ,
529: splitPane, vertical ? IMAGE_ID_VERTICAL_SEPARATOR
530: : IMAGE_ID_HORIZONTAL_SEPARATOR,
531: separatorImage, 0);
532: initElement.setAttribute("separator-image",
533: fillImageCssStyle.renderInline());
534: }
535:
536: Component[] children = splitPane.getVisibleComponents();
537: for (int i = 0; i < children.length; ++i) {
538: renderLayoutData(rc, initElement, children[i], i);
539: }
540:
541: partElement.appendChild(initElement);
542: }
543:
544: private void renderLayoutData(RenderContext rc,
545: Element containerElement, Component component, int index) {
546: SplitPaneLayoutData layoutData = (SplitPaneLayoutData) component
547: .getRenderProperty(SplitPane.PROPERTY_LAYOUT_DATA);
548: if (layoutData == null) {
549: return;
550: }
551: Element layoutDataElement = rc.getServerMessage().getDocument()
552: .createElement("layout-data");
553: layoutDataElement
554: .setAttribute("index", Integer.toString(index));
555: if (layoutData.getAlignment() != null) {
556: CssStyle alignmentStyle = new CssStyle();
557: AlignmentRender.renderToStyle(alignmentStyle, layoutData
558: .getAlignment(), component);
559: layoutDataElement.setAttribute("alignment", alignmentStyle
560: .renderInline());
561: }
562: if (layoutData.getBackground() != null) {
563: layoutDataElement.setAttribute("background",
564: ColorRender.renderCssAttributeValue(layoutData
565: .getBackground()));
566: }
567: if (layoutData.getBackgroundImage() != null) {
568: CssStyle backgroundImageStyle = new CssStyle();
569: FillImageRender.renderToStyle(backgroundImageStyle, rc,
570: this , component.getParent(),
571: index == 0 ? IMAGE_ID_PANE_0_BACKGROUND
572: : IMAGE_ID_PANE_1_BACKGROUND, layoutData
573: .getBackgroundImage(), 0);
574: layoutDataElement.setAttribute("background-image",
575: backgroundImageStyle.renderInline());
576: }
577: if (!(component instanceof Pane)
578: && layoutData.getInsets() != null) {
579: layoutDataElement.setAttribute("insets", InsetsRender
580: .renderCssAttributeValue(layoutData.getInsets()));
581: }
582: switch (layoutData.getOverflow()) {
583: case SplitPaneLayoutData.OVERFLOW_AUTO:
584: layoutDataElement.setAttribute("overflow", "auto");
585: break;
586: case SplitPaneLayoutData.OVERFLOW_HIDDEN:
587: layoutDataElement.setAttribute("overflow", "hidden");
588: break;
589: case SplitPaneLayoutData.OVERFLOW_SCROLL:
590: layoutDataElement.setAttribute("overflow", "scroll");
591: break;
592: }
593: if (layoutData.getMinimumSize() != null) {
594: layoutDataElement.setAttribute("min-size", Integer
595: .toString(ExtentRender.toPixels(layoutData
596: .getMinimumSize(), -1)));
597: }
598: if (layoutData.getMaximumSize() != null) {
599: layoutDataElement.setAttribute("max-size", Integer
600: .toString(ExtentRender.toPixels(layoutData
601: .getMaximumSize(), -1)));
602: }
603:
604: containerElement.appendChild(layoutDataElement);
605: }
606:
607: private void renderRemoveChildren(RenderContext rc,
608: ServerComponentUpdate update) {
609: ContainerInstance ci = rc.getContainerInstance();
610: SplitPane splitPane = (SplitPane) update.getParent();
611: RenderStateImpl previousRenderState = (RenderStateImpl) ci
612: .getRenderState(splitPane);
613:
614: RenderStateImpl currentRenderState = new RenderStateImpl(
615: splitPane);
616:
617: if (!equal(previousRenderState.pane0, currentRenderState.pane0)) {
618: if (previousRenderState.pane0 != null) {
619: renderRemoveChildDirective(rc, (SplitPane) splitPane, 0);
620: }
621: }
622: if (!equal(previousRenderState.pane1, currentRenderState.pane1)) {
623: if (previousRenderState.pane1 != null) {
624: renderRemoveChildDirective(rc, (SplitPane) splitPane, 1);
625: }
626: }
627: }
628:
629: private void renderRemoveChildDirective(RenderContext rc,
630: SplitPane splitPane, int index) {
631: String elementId = ContainerInstance.getElementId(splitPane);
632: ServerMessage serverMessage = rc.getServerMessage();
633: Element partElement = serverMessage.addPart(
634: ServerMessage.GROUP_ID_REMOVE,
635: "EchoSplitPane.MessageProcessor");
636: Element removeChildElement = serverMessage.getDocument()
637: .createElement("remove-child");
638: removeChildElement.setAttribute("eid", elementId);
639: removeChildElement.setAttribute("index", Integer
640: .toString(index));
641: partElement.appendChild(removeChildElement);
642: }
643:
644: private void renderSetSeparatorPositionDirective(RenderContext rc,
645: SplitPane splitPane) {
646: String elementId = ContainerInstance.getElementId(splitPane);
647: ServerMessage serverMessage = rc.getServerMessage();
648: Element partElement = serverMessage.addPart(
649: ServerMessage.GROUP_ID_REMOVE,
650: "EchoSplitPane.MessageProcessor");
651: Element setSeparatorPositionElement = serverMessage
652: .getDocument().createElement("set-separator-position");
653: setSeparatorPositionElement.setAttribute("eid", elementId);
654: setSeparatorPositionElement.setAttribute("position", Integer
655: .toString(getSeparatorPosition(splitPane)));
656: partElement.appendChild(setSeparatorPositionElement);
657: }
658:
659: /**
660: * @see nextapp.echo2.webcontainer.ComponentSynchronizePeer#renderUpdate(nextapp.echo2.webcontainer.RenderContext,
661: * nextapp.echo2.app.update.ServerComponentUpdate, java.lang.String)
662: */
663: public boolean renderUpdate(RenderContext rc,
664: ServerComponentUpdate update, String targetId) {
665: boolean fullReplace = false;
666: if (update.hasUpdatedLayoutDataChildren()) {
667: fullReplace = true;
668: } else if (update.hasUpdatedProperties()) {
669: if (!partialUpdateManager.canProcess(rc, update)) {
670: fullReplace = true;
671: }
672: }
673:
674: if (fullReplace) {
675: // Perform full update.
676: renderDisposeDirective(rc, (SplitPane) update.getParent());
677: DomUpdate.renderElementRemove(rc.getServerMessage(),
678: ContainerInstance.getElementId(update.getParent()));
679: renderAdd(rc, update, targetId, update.getParent());
680: } else {
681: partialUpdateManager.process(rc, update);
682: if (update.hasAddedChildren()
683: || update.hasRemovedChildren()) {
684: renderRemoveChildren(rc, update);
685: renderAddChildren(rc, update);
686: }
687: }
688:
689: updateRenderState(rc, (SplitPane) update.getParent());
690: return fullReplace;
691: }
692:
693: /**
694: * Update the stored <code>RenderState</code>.
695: *
696: * @param rc the relevant <code>RenderContext</code>
697: * @param splitPane the <code>SplitPane</code> component
698: */
699: private void updateRenderState(RenderContext rc, SplitPane splitPane) {
700: rc.getContainerInstance().setRenderState(splitPane,
701: new RenderStateImpl(splitPane));
702: }
703: }
|