KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > nextapp > echo2 > webcontainer > syncpeer > ContentPanePeer


1 /*
2  * This file is part of the Echo Web Application Framework (hereinafter "Echo").
3  * Copyright (C) 2002-2005 NextApp, Inc.
4  *
5  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * Alternatively, the contents of this file may be used under the terms of
18  * either the GNU General Public License Version 2 or later (the "GPL"), or
19  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
20  * in which case the provisions of the GPL or the LGPL are applicable instead
21  * of those above. If you wish to allow use of your version of this file only
22  * under the terms of either the GPL or the LGPL, and not to allow others to
23  * use your version of this file under the terms of the MPL, indicate your
24  * decision by deleting the provisions above and replace them with the notice
25  * and other provisions required by the GPL or the LGPL. If you do not delete
26  * the provisions above, a recipient may use your version of this file under
27  * the terms of any one of the MPL, the GPL or the LGPL.
28  */

29
30 package nextapp.echo2.webcontainer.syncpeer;
31
32 import org.w3c.dom.Document JavaDoc;
33 import org.w3c.dom.DocumentFragment JavaDoc;
34 import org.w3c.dom.Element JavaDoc;
35 import org.w3c.dom.Node JavaDoc;
36
37 import nextapp.echo2.app.Color;
38 import nextapp.echo2.app.Component;
39 import nextapp.echo2.app.ContentPane;
40 import nextapp.echo2.app.Extent;
41 import nextapp.echo2.app.FillImage;
42 import nextapp.echo2.app.FloatingPane;
43 import nextapp.echo2.app.Font;
44 import nextapp.echo2.app.ImageReference;
45 import nextapp.echo2.app.Insets;
46 import nextapp.echo2.app.button.AbstractButton;
47 import nextapp.echo2.app.update.ServerComponentUpdate;
48 import nextapp.echo2.webcontainer.ContainerInstance;
49 import nextapp.echo2.webcontainer.DomUpdateSupport;
50 import nextapp.echo2.webcontainer.PartialUpdateManager;
51 import nextapp.echo2.webcontainer.PartialUpdateParticipant;
52 import nextapp.echo2.webcontainer.PropertyUpdateProcessor;
53 import nextapp.echo2.webcontainer.RenderContext;
54 import nextapp.echo2.webcontainer.ComponentSynchronizePeer;
55 import nextapp.echo2.webcontainer.SynchronizePeerFactory;
56 import nextapp.echo2.webcontainer.image.ImageRenderSupport;
57 import nextapp.echo2.webcontainer.propertyrender.ColorRender;
58 import nextapp.echo2.webcontainer.propertyrender.ExtentRender;
59 import nextapp.echo2.webcontainer.propertyrender.FillImageRender;
60 import nextapp.echo2.webcontainer.propertyrender.FontRender;
61 import nextapp.echo2.webrender.ServerMessage;
62 import nextapp.echo2.webrender.Service;
63 import nextapp.echo2.webrender.WebRenderServlet;
64 import nextapp.echo2.webrender.output.CssStyle;
65 import nextapp.echo2.webrender.servermessage.DomUpdate;
66 import nextapp.echo2.webrender.servermessage.VirtualPosition;
67 import nextapp.echo2.webrender.service.JavaScriptService;
68
69 /**
70  * Synchronization peer for <code>nextapp.echo2.app.ContentPane</code> components.
71  * <p>
72  * This class should not be extended or used by classes outside of the
73  * Echo framework.
74  */

75 public class ContentPanePeer
76 implements ComponentSynchronizePeer, DomUpdateSupport, ImageRenderSupport, PropertyUpdateProcessor {
77
78     //TODO: This needs to become a client-rendered component.
79
//TODO: Performance can be improved by implementing MORE PartialUpdateManagers.
80

81     private static final Extent EXTENT_0 = new Extent(0);
82     private static final Insets DEFAULT_INSETS = new Insets(EXTENT_0);
83     
84     private static final String JavaDoc IMAGE_ID_BACKGROUND = "background";
85
86     /**
87      * Service to provide supporting JavaScript library.
88      */

89     private static final Service CONTENT_PANE_SERVICE = JavaScriptService.forResource("Echo.ContentPane",
90             "/nextapp/echo2/webcontainer/resource/js/ContentPane.js");
91
92     static {
93         WebRenderServlet.getServiceRegistry().add(CONTENT_PANE_SERVICE);
94     }
95     
96     private PartialUpdateManager partialUpdateManager;
97     
98     /**
99      * Default constructor.
100      */

101     public ContentPanePeer() {
102         super();
103         partialUpdateManager = new PartialUpdateManager();
104         partialUpdateManager.add(ContentPane.PROPERTY_HORIZONTAL_SCROLL, new PartialUpdateParticipant() {
105         
106             public void renderProperty(RenderContext rc, ServerComponentUpdate update) {
107                 renderScrollDirective(rc, (ContentPane) update.getParent(), true);
108             }
109         
110             public boolean canRenderProperty(RenderContext rc, ServerComponentUpdate update) {
111                 return true;
112             }
113         });
114         partialUpdateManager.add(ContentPane.PROPERTY_VERTICAL_SCROLL, new PartialUpdateParticipant() {
115         
116             public void renderProperty(RenderContext rc, ServerComponentUpdate update) {
117                 renderScrollDirective(rc, (ContentPane) update.getParent(), false);
118             }
119         
120             public boolean canRenderProperty(RenderContext rc, ServerComponentUpdate update) {
121                 return true;
122             }
123         });
124     }
125     
126     /**
127      * @see nextapp.echo2.webcontainer.ComponentSynchronizePeer#getContainerId(nextapp.echo2.app.Component)
128      */

129     public String JavaDoc getContainerId(Component child) {
130         return getContainerId(child.getParent(), child);
131     }
132     
133     private String JavaDoc getContainerId(Component parent, Component child) {
134         if (child instanceof FloatingPane) {
135             return ContainerInstance.getElementId(parent) + "_float_" + ContainerInstance.getElementId(child);
136         } else {
137             return ContainerInstance.getElementId(parent) + "_content_" + ContainerInstance.getElementId(child);
138         }
139     }
140     
141     /**
142      * @see nextapp.echo2.webcontainer.image.ImageRenderSupport#getImage(nextapp.echo2.app.Component, java.lang.String)
143      */

144     public ImageReference getImage(Component component, String JavaDoc imageId) {
145         if (IMAGE_ID_BACKGROUND.equals(imageId)) {
146             FillImage backgroundImage
147                     = (FillImage) component.getRenderProperty(AbstractButton.PROPERTY_BACKGROUND_IMAGE);
148             if (backgroundImage == null) {
149                 return null;
150             } else {
151                 return backgroundImage.getImage();
152             }
153         } else {
154             return null;
155         }
156     }
157
158     /**
159      * @see nextapp.echo2.webcontainer.ComponentSynchronizePeer#renderAdd(nextapp.echo2.webcontainer.RenderContext,
160      * nextapp.echo2.app.update.ServerComponentUpdate, java.lang.String, nextapp.echo2.app.Component)
161      */

162     public void renderAdd(RenderContext rc, ServerComponentUpdate update, String JavaDoc targetId, Component component) {
163         Element JavaDoc domAddElement = DomUpdate.renderElementAdd(rc.getServerMessage());
164         DocumentFragment JavaDoc htmlFragment = rc.getServerMessage().getDocument().createDocumentFragment();
165         renderHtml(rc, update, htmlFragment, component);
166         DomUpdate.renderElementAddContent(rc.getServerMessage(), domAddElement, targetId, htmlFragment);
167     }
168
169     /**
170      * Renders child components which were added to a
171      * <code>ContentPane</code>, as described in the provided
172      * <code>ServerComponentUpdate</code>.
173      *
174      * @param rc the relevant <code>RenderContext</code>
175      * @param update the update
176      */

177     private void renderAddChildren(RenderContext rc, ServerComponentUpdate update) {
178         ContentPane contentPane = (ContentPane) update.getParent();
179         String JavaDoc elementId = ContainerInstance.getElementId(contentPane);
180         Component[] components = update.getParent().getVisibleComponents();
181         Component[] addedChildren = update.getAddedChildren();
182         
183         for (int componentIndex = components.length - 1; componentIndex >= 0; --componentIndex) {
184             boolean childFound = false;
185             for (int addedChildrenIndex = 0; !childFound && addedChildrenIndex < addedChildren.length; ++addedChildrenIndex) {
186                 if (addedChildren[addedChildrenIndex] == components[componentIndex]) {
187                     Element JavaDoc domAddElement = DomUpdate.renderElementAdd(rc.getServerMessage());
188                     DocumentFragment JavaDoc htmlFragment = rc.getServerMessage().getDocument().createDocumentFragment();
189                     renderChild(rc, update, htmlFragment, contentPane, components[componentIndex]);
190                     
191                     if (componentIndex == components.length - 1) {
192                         DomUpdate.renderElementAddContent(rc.getServerMessage(), domAddElement, elementId, htmlFragment);
193                     } else {
194                         DomUpdate.renderElementAddContent(rc.getServerMessage(), domAddElement,
195                                 elementId, getContainerId(components[componentIndex + 1]), htmlFragment);
196                     }
197
198                     childFound = true;
199                 }
200             }
201         }
202     }
203     
204     /**
205      * Renders an individual child component of the <code>ContentPane</code>.
206      *
207      * @param rc the relevant <code>RenderContext</code>
208      * @param update the <code>ServerComponentUpdate</code> being performed
209      * @param parentNode the outer &lt;div&gt; element of the
210      * <code>ContentPane</code>
211      * @param contentPane the containing <code>ContentPane</code>
212      * @param child the child <code>Component</code> to be rendered
213      */

214     private void renderChild(RenderContext rc, ServerComponentUpdate update, Node JavaDoc parentNode,
215             ContentPane contentPane, Component child) {
216         Element JavaDoc containerDivElement = parentNode.getOwnerDocument().createElement("div");
217         String JavaDoc containerId = getContainerId(child);
218         containerDivElement.setAttribute("id", containerId);
219         if (!(child instanceof FloatingPane)) {
220             CssStyle style = new CssStyle();
221             style.setAttribute("position", "absolute");
222             style.setAttribute("overflow", "auto");
223             style.setAttribute("z-index", "0");
224             Insets insets = (Insets) contentPane.getRenderProperty(ContentPane.PROPERTY_INSETS, DEFAULT_INSETS);
225             style.setAttribute("top", ExtentRender.renderCssAttributePixelValue(insets.getTop(), "0"));
226             style.setAttribute("left", ExtentRender.renderCssAttributePixelValue(insets.getLeft(), "0"));
227             style.setAttribute("right", ExtentRender.renderCssAttributePixelValue(insets.getRight(), "0"));
228             style.setAttribute("bottom", ExtentRender.renderCssAttributePixelValue(insets.getBottom(), "0"));
229             containerDivElement.setAttribute("style", style.renderInline());
230             VirtualPosition.renderRegister(rc.getServerMessage(), containerId);
231         }
232         
233         parentNode.appendChild(containerDivElement);
234         ComponentSynchronizePeer syncPeer = SynchronizePeerFactory.getPeerForComponent(child.getClass());
235         if (syncPeer instanceof DomUpdateSupport) {
236             ((DomUpdateSupport) syncPeer).renderHtml(rc, update, containerDivElement, child);
237         } else {
238             syncPeer.renderAdd(rc, update, containerId, child);
239         }
240     }
241     
242     /**
243      * @see nextapp.echo2.webcontainer.ComponentSynchronizePeer#renderDispose(nextapp.echo2.webcontainer.RenderContext,
244      * nextapp.echo2.app.update.ServerComponentUpdate, nextapp.echo2.app.Component)
245      */

246     public void renderDispose(RenderContext rc, ServerComponentUpdate update, Component component) {
247         rc.getServerMessage().addLibrary(CONTENT_PANE_SERVICE.getId());
248         renderDisposeDirective(rc, (ContentPane) component);
249     }
250     
251     /**
252      * Renders a directive to the outgoing <code>ServerMessage</code> to
253      * dispose the state of a <code>ContentPane</code>, performing tasks such as
254      * unregistering event listeners on the client.
255      *
256      * @param rc the relevant <code>RenderContext</code>
257      * @param contentPane the <code>ContentPane</code>
258      */

259     private void renderDisposeDirective(RenderContext rc, ContentPane contentPane) {
260         ServerMessage serverMessage = rc.getServerMessage();
261         Element JavaDoc itemizedUpdateElement = serverMessage.getItemizedDirective(ServerMessage.GROUP_ID_PREREMOVE,
262                 "EchoContentPane.MessageProcessor", "dispose", new String JavaDoc[0], new String JavaDoc[0]);
263         Element JavaDoc itemElement = serverMessage.getDocument().createElement("item");
264         itemElement.setAttribute("eid", ContainerInstance.getElementId(contentPane));
265         itemizedUpdateElement.appendChild(itemElement);
266     }
267     
268     /**
269      * @see nextapp.echo2.webcontainer.DomUpdateSupport#renderHtml(nextapp.echo2.webcontainer.RenderContext,
270      * nextapp.echo2.app.update.ServerComponentUpdate, org.w3c.dom.Node, nextapp.echo2.app.Component)
271      */

272     public void renderHtml(RenderContext rc, ServerComponentUpdate update, Node JavaDoc parentNode, Component component) {
273         ContentPane contentPane = (ContentPane) component;
274         
275         ServerMessage serverMessage = rc.getServerMessage();
276         serverMessage.addLibrary(CONTENT_PANE_SERVICE.getId());
277
278         Document JavaDoc document = parentNode.getOwnerDocument();
279         Element JavaDoc divElement = document.createElement("div");
280         divElement.setAttribute("id", ContainerInstance.getElementId(component));
281         
282         CssStyle cssStyle = new CssStyle();
283         cssStyle.setAttribute("position", "absolute");
284         cssStyle.setAttribute("width", "100%");
285         cssStyle.setAttribute("height", "100%");
286         cssStyle.setAttribute("overflow", "hidden");
287         cssStyle.setAttribute("z-index", "0");
288         ColorRender.renderToStyle(cssStyle, (Color) contentPane.getRenderProperty(ContentPane.PROPERTY_FOREGROUND),
289                 (Color) contentPane.getRenderProperty(ContentPane.PROPERTY_BACKGROUND));
290         FontRender.renderToStyle(cssStyle, (Font) contentPane.getRenderProperty(ContentPane.PROPERTY_FONT));
291         FillImageRender.renderToStyle(cssStyle, rc, this, contentPane, IMAGE_ID_BACKGROUND,
292                 (FillImage) contentPane.getRenderProperty(ContentPane.PROPERTY_BACKGROUND_IMAGE), 0);
293         divElement.setAttribute("style", cssStyle.renderInline());
294         
295         parentNode.appendChild(divElement);
296
297         // Render initialization directive.
298
renderInitDirective(rc, contentPane);
299         
300         Component[] children = contentPane.getVisibleComponents();
301         for (int i = 0; i < children.length; ++i) {
302             renderChild(rc, update, divElement, contentPane, children[i]);
303         }
304     }
305
306     /**
307      * Renders a directive to the outgoing <code>ServerMessage</code> to
308      * initialize the state of a <code>ContentPane</code>, performing tasks
309      * such as registering event listeners on the client.
310      *
311      * @param rc the relevant <code>RenderContext</code>
312      * @param contentPane the <code>ContentPane</code>
313      */

314     private void renderInitDirective(RenderContext rc, ContentPane contentPane) {
315         String JavaDoc elementId = ContainerInstance.getElementId(contentPane);
316         ServerMessage serverMessage = rc.getServerMessage();
317
318         Element JavaDoc itemizedUpdateElement = serverMessage.getItemizedDirective(ServerMessage.GROUP_ID_POSTUPDATE,
319                 "EchoContentPane.MessageProcessor", "init", new String JavaDoc[0], new String JavaDoc[0]);
320         Element JavaDoc itemElement = serverMessage.getDocument().createElement("item");
321         itemElement.setAttribute("eid", elementId);
322         Extent horizontalScroll = (Extent) contentPane.getRenderProperty(ContentPane.PROPERTY_HORIZONTAL_SCROLL);
323         if (horizontalScroll != null && horizontalScroll.getValue() != 0) {
324             itemElement.setAttribute("horizontal-scroll", ExtentRender.renderCssAttributeValue(horizontalScroll));
325         }
326         Extent verticalScroll = (Extent) contentPane.getRenderProperty(ContentPane.PROPERTY_VERTICAL_SCROLL);
327         if (verticalScroll != null && verticalScroll.getValue() != 0) {
328             itemElement.setAttribute("vertical-scroll", ExtentRender.renderCssAttributeValue(verticalScroll));
329         }
330         itemizedUpdateElement.appendChild(itemElement);
331     }
332     
333     /**
334      * Renders a directive to the outgoing <code>ServerMessage</code> to update
335      * the scroll bar positions of a <code>ContentPane</code>.
336      *
337      * @param rc the relevant <code>RenderContext</code>
338      * @param contentPane the <code>ContentPane</code>
339      * @param horizontal a flag indicating whether the horizontal (true) or
340      * vertical (false) scroll bar position should be updated
341      */

342     private void renderScrollDirective(RenderContext rc, ContentPane contentPane, boolean horizontal) {
343         ServerMessage serverMessage = rc.getServerMessage();
344         Element JavaDoc scrollElement =
345                 serverMessage.appendPartDirective(ServerMessage.GROUP_ID_POSTUPDATE, "EchoContentPane.MessageProcessor",
346                 horizontal ? "scroll-horizontal" : "scroll-vertical");
347         Extent position = (Extent) contentPane.getRenderProperty(
348                 horizontal ? ContentPane.PROPERTY_HORIZONTAL_SCROLL : ContentPane.PROPERTY_VERTICAL_SCROLL, EXTENT_0);
349         scrollElement.setAttribute("eid", ContainerInstance.getElementId(contentPane));
350         scrollElement.setAttribute("position", ExtentRender.renderCssAttributeValue(position));
351     }
352
353     /**
354      * Renders removal operations for child components which were removed from
355      * a <code>ContentPane</code>, as described in the provided
356      * <code>ServerComponentUpdate</code>.
357      *
358      * @param rc the relevant <code>RenderContext</code>
359      * @param update the update
360      */

361     private void renderRemoveChildren(RenderContext rc, ServerComponentUpdate update) {
362         Component[] removedChildren = update.getRemovedChildren();
363         for (int i = 0; i < removedChildren.length; ++i) {
364             DomUpdate.renderElementRemove(rc.getServerMessage(), getContainerId(update.getParent(), removedChildren[i]));
365         }
366     }
367     /**
368      * @see nextapp.echo2.webcontainer.ComponentSynchronizePeer#renderUpdate(nextapp.echo2.webcontainer.RenderContext,
369      * nextapp.echo2.app.update.ServerComponentUpdate, java.lang.String)
370      */

371     public boolean renderUpdate(RenderContext rc, ServerComponentUpdate update, String JavaDoc targetId) {
372         boolean fullReplace = false;
373         if (update.hasUpdatedLayoutDataChildren()) {
374             fullReplace = true;
375         } else if (update.hasUpdatedProperties()) {
376             if (!partialUpdateManager.canProcess(rc, update)) {
377                 fullReplace = true;
378             }
379         }
380         
381         if (fullReplace) {
382             // Perform full update.
383
DomUpdate.renderElementRemove(rc.getServerMessage(),
384                     ContainerInstance.getElementId(update.getParent()));
385             renderAdd(rc, update, targetId, update.getParent());
386         } else {
387             partialUpdateManager.process(rc, update);
388             if (update.hasAddedChildren() || update.hasRemovedChildren()) {
389                 renderContentChange(rc, update);
390             }
391         }
392         return fullReplace;
393     }
394     
395     /**
396      * Processes a change to the content of a <code>ContentPane</code>.
397      * This method will invoke <code>renderRemoveChildren()</code> and
398      * <code>renderAddChildren()</cdoe>. If the main content has changed,
399      * i.e., the non-<code>FloatingPane</code> child, it will unregister
400      * and re-register scrolling listeners by rendering dispose and then
401      * init directives.
402      *
403      * @param rc the relevant <code>RenderContext</code>
404      * @param update the update
405      */

406     private void renderContentChange(RenderContext rc, ServerComponentUpdate update) {
407         boolean primaryContentChanged = false;
408         Component[] addedChildren = update.getAddedChildren();
409         for (int i = 0; i < addedChildren.length; ++i) {
410             if (!(addedChildren[i] instanceof FloatingPane)) {
411                 primaryContentChanged = true;
412                 break;
413             }
414         }
415         if (!primaryContentChanged) {
416             Component[] removedChildren = update.getRemovedChildren();
417             for (int i = 0; i < removedChildren.length; ++i) {
418                 if (!(removedChildren[i] instanceof FloatingPane)) {
419                     primaryContentChanged = true;
420                     break;
421                 }
422             }
423         }
424         
425         if (primaryContentChanged) {
426             renderDisposeDirective(rc, (ContentPane) update.getParent());
427         }
428
429         renderRemoveChildren(rc, update);
430         renderAddChildren(rc, update);
431
432         if (primaryContentChanged) {
433             renderInitDirective(rc, (ContentPane) update.getParent());
434         }
435     }
436     
437     /**
438      * @see nextapp.echo2.webcontainer.PropertyUpdateProcessor#processPropertyUpdate(
439      * nextapp.echo2.webcontainer.ContainerInstance,
440      * nextapp.echo2.app.Component, org.w3c.dom.Element)
441      */

442     public void processPropertyUpdate(ContainerInstance ci, Component component, Element JavaDoc propertyElement) {
443         if ("horizontalScroll".equals(propertyElement.getAttribute(PropertyUpdateProcessor.PROPERTY_NAME))) {
444             Extent newValue = ExtentRender.toExtent(propertyElement.getAttribute(PropertyUpdateProcessor.PROPERTY_VALUE));
445             ci.getUpdateManager().getClientUpdateManager().setComponentProperty(component,
446                     ContentPane.PROPERTY_HORIZONTAL_SCROLL, newValue);
447         } else if ("verticalScroll".equals(propertyElement.getAttribute(PropertyUpdateProcessor.PROPERTY_NAME))) {
448             Extent newValue = ExtentRender.toExtent(propertyElement.getAttribute(PropertyUpdateProcessor.PROPERTY_VALUE));
449             ci.getUpdateManager().getClientUpdateManager().setComponentProperty(component,
450                     ContentPane.PROPERTY_VERTICAL_SCROLL, newValue);
451         }
452     }
453 }
454
Popular Tags