1 29 30 package nextapp.echo2.webcontainer.syncpeer; 31 32 import nextapp.echo2.app.FillImage; 33 import nextapp.echo2.app.Color; 34 import nextapp.echo2.app.Component; 35 import nextapp.echo2.app.Extent; 36 import nextapp.echo2.app.Font; 37 import nextapp.echo2.app.ImageReference; 38 import nextapp.echo2.app.LayoutData; 39 import nextapp.echo2.app.LayoutDirection; 40 import nextapp.echo2.app.Pane; 41 import nextapp.echo2.app.SplitPane; 42 import nextapp.echo2.app.layout.SplitPaneLayoutData; 43 import nextapp.echo2.app.update.ServerComponentUpdate; 44 import nextapp.echo2.webcontainer.ContainerInstance; 45 import nextapp.echo2.webcontainer.PartialUpdateManager; 46 import nextapp.echo2.webcontainer.PartialUpdateParticipant; 47 import nextapp.echo2.webcontainer.PropertyUpdateProcessor; 48 import nextapp.echo2.webcontainer.RenderContext; 49 import nextapp.echo2.webcontainer.ComponentSynchronizePeer; 50 import nextapp.echo2.webcontainer.RenderState; 51 import nextapp.echo2.webcontainer.SynchronizePeerFactory; 52 import nextapp.echo2.webcontainer.image.ImageRenderSupport; 53 import nextapp.echo2.webcontainer.propertyrender.AlignmentRender; 54 import nextapp.echo2.webcontainer.propertyrender.ColorRender; 55 import nextapp.echo2.webcontainer.propertyrender.ExtentRender; 56 import nextapp.echo2.webcontainer.propertyrender.FillImageRender; 57 import nextapp.echo2.webcontainer.propertyrender.FontRender; 58 import nextapp.echo2.webcontainer.propertyrender.InsetsRender; 59 import nextapp.echo2.webrender.ServerMessage; 60 import nextapp.echo2.webrender.Service; 61 import nextapp.echo2.webrender.WebRenderServlet; 62 import nextapp.echo2.webrender.output.CssStyle; 63 import nextapp.echo2.webrender.servermessage.DomUpdate; 64 import nextapp.echo2.webrender.service.JavaScriptService; 65 66 import org.w3c.dom.Element ; 67 68 74 public class SplitPanePeer 75 implements ImageRenderSupport, PropertyUpdateProcessor, ComponentSynchronizePeer { 76 77 79 private static final String IMAGE_ID_HORIZONTAL_SEPARATOR = "horizontalSeparator"; 80 private static final String IMAGE_ID_PANE_0_BACKGROUND = "pane0Background"; 81 private static final String IMAGE_ID_PANE_1_BACKGROUND = "pane1Background"; 82 private static final String IMAGE_ID_VERTICAL_SEPARATOR = "verticalSeparator"; 83 84 private static final Extent DEFAULT_SEPARATOR_POSITION = new Extent(100); 85 86 89 static class RenderStateImpl 90 implements RenderState { 91 92 95 private String pane0; 96 97 100 private String pane1; 101 102 108 private RenderStateImpl(SplitPane splitPane) { 109 int componentCount = splitPane.getVisibleComponentCount(); 110 pane0 = (componentCount < 1 || splitPane.getVisibleComponent(0) == null) 111 ? null : ContainerInstance.getElementId(splitPane.getVisibleComponent(0)); 112 pane1 = (componentCount < 2 || splitPane.getVisibleComponent(1) == null) 113 ? null : ContainerInstance.getElementId(splitPane.getVisibleComponent(1)); 114 } 115 } 116 117 120 private static final Service SPLIT_PANE_SERVICE = JavaScriptService.forResource("Echo.SplitPane", 121 "/nextapp/echo2/webcontainer/resource/js/SplitPane.js"); 122 123 static { 124 WebRenderServlet.getServiceRegistry().add(SPLIT_PANE_SERVICE); 125 } 126 127 130 private static final boolean equal(Object a, Object b) { 131 return a == b || (a != null && a.equals(b)); 132 } 133 134 137 private PartialUpdateParticipant separatorPositionUpdate = new PartialUpdateParticipant() { 138 139 142 public void renderProperty(RenderContext rc, ServerComponentUpdate update) { 143 SplitPane splitPane = (SplitPane) update.getParent(); 144 renderSetSeparatorPositionDirective(rc, splitPane); 145 } 146 147 150 public boolean canRenderProperty(RenderContext rc, ServerComponentUpdate update) { 151 return true; 152 } 153 154 }; 155 156 private PartialUpdateManager partialUpdateManager; 157 158 161 public SplitPanePeer() { 162 super(); 163 partialUpdateManager = new PartialUpdateManager(); 164 partialUpdateManager.add(SplitPane.PROPERTY_SEPARATOR_POSITION, separatorPositionUpdate); 165 } 166 167 173 private int calculateSeparatorSize(SplitPane splitPane) { 174 Boolean booleanValue = (Boolean ) splitPane.getRenderProperty(SplitPane.PROPERTY_RESIZABLE); 175 boolean resizable = booleanValue == null ? false : booleanValue.booleanValue(); 176 boolean verticalOrientation = isOrientationVertical(splitPane); 177 if (resizable) { 178 return ExtentRender.toPixels((Extent) splitPane.getRenderProperty( 179 verticalOrientation ? SplitPane.PROPERTY_SEPARATOR_HEIGHT : SplitPane.PROPERTY_SEPARATOR_WIDTH), 4); 180 } else { 181 return ExtentRender.toPixels((Extent) splitPane.getRenderProperty( 182 verticalOrientation ? SplitPane.PROPERTY_SEPARATOR_HEIGHT : SplitPane.PROPERTY_SEPARATOR_WIDTH), 0); 183 } 184 } 185 186 189 public String getContainerId(Component child) { 190 return ContainerInstance.getElementId(child.getParent()) + "_pane" + child.getParent().visibleIndexOf(child); 191 } 192 193 196 public ImageReference getImage(Component component, String imageId) { 197 if (IMAGE_ID_PANE_0_BACKGROUND.equals(imageId)) { 198 return getPaneBackgroundImage(component.getVisibleComponent(0)); 199 } else if (IMAGE_ID_PANE_1_BACKGROUND.equals(imageId)) { 200 return getPaneBackgroundImage(component.getVisibleComponent(1)); 201 } else if (IMAGE_ID_HORIZONTAL_SEPARATOR.equals(imageId)) { 202 FillImage fillImage = (FillImage) component.getRenderProperty(SplitPane.PROPERTY_SEPARATOR_HORIZONTAL_IMAGE); 203 return fillImage == null ? null : fillImage.getImage(); 204 } else if (IMAGE_ID_VERTICAL_SEPARATOR.equals(imageId)) { 205 FillImage fillImage = (FillImage) component.getRenderProperty(SplitPane.PROPERTY_SEPARATOR_VERTICAL_IMAGE); 206 return fillImage == null ? null : fillImage.getImage(); 207 } else { 208 return null; 209 } 210 } 211 212 220 private ImageReference getPaneBackgroundImage(Component component) { 221 LayoutData layoutData = (LayoutData) component.getRenderProperty(SplitPane.PROPERTY_LAYOUT_DATA); 222 if (!(layoutData instanceof SplitPaneLayoutData)) { 224 return null; 225 } 226 FillImage backgroundImage = ((SplitPaneLayoutData) layoutData).getBackgroundImage(); 227 if (backgroundImage == null) { 228 return null; 229 } 230 return backgroundImage.getImage(); 231 } 232 233 private int getRenderOrientation(SplitPane splitPane) { 234 Integer orientationValue = (Integer ) splitPane.getRenderProperty(SplitPane.PROPERTY_ORIENTATION); 235 int orientation = orientationValue == null ? SplitPane.ORIENTATION_HORIZONTAL_LEADING_TRAILING 236 : orientationValue.intValue(); 237 if (orientation == SplitPane.ORIENTATION_HORIZONTAL_LEADING_TRAILING 238 || orientation == SplitPane.ORIENTATION_HORIZONTAL_TRAILING_LEADING) { 239 LayoutDirection layoutDirection = splitPane.getRenderLayoutDirection(); 240 if (orientation == SplitPane.ORIENTATION_HORIZONTAL_LEADING_TRAILING) { 241 orientation = layoutDirection.isLeftToRight() ? SplitPane.ORIENTATION_HORIZONTAL_LEFT_RIGHT 242 : SplitPane.ORIENTATION_HORIZONTAL_RIGHT_LEFT; 243 } else { 244 orientation = layoutDirection.isLeftToRight() ? SplitPane.ORIENTATION_HORIZONTAL_RIGHT_LEFT 245 : SplitPane.ORIENTATION_HORIZONTAL_LEFT_RIGHT; 246 } 247 } 248 return orientation; 249 } 250 251 258 private boolean isOrientationVertical(SplitPane splitPane) { 259 Integer orientationValue = (Integer ) splitPane.getRenderProperty(SplitPane.PROPERTY_ORIENTATION); 260 int orientation = orientationValue == null ? SplitPane.ORIENTATION_HORIZONTAL : orientationValue.intValue(); 261 return orientation == SplitPane.ORIENTATION_VERTICAL_TOP_BOTTOM || 262 orientation == SplitPane.ORIENTATION_VERTICAL_BOTTOM_TOP; 263 } 264 265 269 public void processPropertyUpdate(ContainerInstance ci, Component component, Element propertyElement) { 270 if ("separatorPosition".equals(propertyElement.getAttribute(PropertyUpdateProcessor.PROPERTY_NAME))) { 271 Extent newValue = ExtentRender.toExtent(propertyElement.getAttribute(PropertyUpdateProcessor.PROPERTY_VALUE)); 272 ci.getUpdateManager().getClientUpdateManager().setComponentProperty(component, 273 SplitPane.PROPERTY_SEPARATOR_POSITION, newValue); 274 } 275 } 276 277 281 public void renderAdd(RenderContext rc, ServerComponentUpdate update, String targetId, Component component) { 282 ServerMessage serverMessage = rc.getServerMessage(); 283 serverMessage.addLibrary(SPLIT_PANE_SERVICE.getId()); 284 SplitPane splitPane = (SplitPane) component; 285 renderInitDirective(rc, splitPane, targetId); 286 Component[] children = splitPane.getVisibleComponents(); 287 for (int i = 0; i < children.length; ++i) { 288 renderChild(rc, update, splitPane, children[i]); 289 } 290 updateRenderState(rc, splitPane); 291 } 292 293 private void renderAddChildDirective(RenderContext rc, ServerComponentUpdate update, SplitPane splitPane, int index) { 294 String elementId = ContainerInstance.getElementId(splitPane); 295 ServerMessage serverMessage = rc.getServerMessage(); 296 Element partElement = serverMessage.addPart(ServerMessage.GROUP_ID_UPDATE, "EchoSplitPane.MessageProcessor"); 297 Element addChildElement = serverMessage.getDocument().createElement("add-child"); 298 addChildElement.setAttribute("eid", elementId); 299 addChildElement.setAttribute("index", Integer.toString(index)); 300 Component child = splitPane.getVisibleComponent(index); 301 renderLayoutData(rc, addChildElement, child, index); 302 partElement.appendChild(addChildElement); 303 renderChild(rc, update, splitPane, child); 304 } 305 306 314 private void renderAddChildren(RenderContext rc, ServerComponentUpdate update) { 315 ContainerInstance ci = rc.getContainerInstance(); 316 SplitPane splitPane = (SplitPane) update.getParent(); 317 318 RenderStateImpl previousRenderState = (RenderStateImpl) ci.getRenderState(splitPane); 319 RenderStateImpl currentRenderState = new RenderStateImpl(splitPane); 320 if (!equal(previousRenderState.pane0, currentRenderState.pane0)) { 321 if (currentRenderState.pane0 != null) { 322 renderAddChildDirective(rc, update, splitPane, 0); 323 } 324 } 325 if (!equal(previousRenderState.pane1, currentRenderState.pane1)) { 326 if (currentRenderState.pane1 != null) { 327 renderAddChildDirective(rc, update, splitPane, 1); 328 } 329 } 330 } 331 332 339 private void renderChild(RenderContext rc, ServerComponentUpdate update, SplitPane splitPane, Component child) { 340 ComponentSynchronizePeer syncPeer = SynchronizePeerFactory.getPeerForComponent(child.getClass()); 341 syncPeer.renderAdd(rc, update, getContainerId(child), child); 342 } 343 344 348 public void renderDispose(RenderContext rc, ServerComponentUpdate update, Component component) { 349 ServerMessage serverMessage = rc.getServerMessage(); 350 serverMessage.addLibrary(SPLIT_PANE_SERVICE.getId()); 351 renderDisposeDirective(rc, (SplitPane) component); 352 353 } 363 364 372 private void renderDisposeDirective(RenderContext rc, SplitPane splitPane) { 373 String elementId = ContainerInstance.getElementId(splitPane); 374 ServerMessage serverMessage = rc.getServerMessage(); 375 Element initElement = serverMessage.appendPartDirective(ServerMessage.GROUP_ID_PREREMOVE, 376 "EchoSplitPane.MessageProcessor", "dispose"); 377 initElement.setAttribute("eid", elementId); 378 } 379 380 private int getSeparatorPosition(SplitPane splitPane) { 381 Extent separatorPosition = (Extent) splitPane.getRenderProperty(SplitPane.PROPERTY_SEPARATOR_POSITION, 382 DEFAULT_SEPARATOR_POSITION); 383 return ExtentRender.toPixels(separatorPosition, 100); 384 } 385 386 394 private void renderInitDirective(RenderContext rc, SplitPane splitPane, String targetId) { 395 String elementId = ContainerInstance.getElementId(splitPane); 396 boolean vertical = isOrientationVertical(splitPane); 397 ServerMessage serverMessage = rc.getServerMessage(); 398 Element partElement = serverMessage.addPart(ServerMessage.GROUP_ID_UPDATE, "EchoSplitPane.MessageProcessor"); 399 Element initElement = serverMessage.getDocument().createElement("init"); 400 initElement.setAttribute("container-eid", targetId); 401 initElement.setAttribute("eid", elementId); 402 403 initElement.setAttribute("position", Integer.toString(getSeparatorPosition(splitPane))); 404 405 int orientation = getRenderOrientation(splitPane); 406 switch (orientation) { 407 case SplitPane.ORIENTATION_HORIZONTAL_LEFT_RIGHT: 408 initElement.setAttribute("orientation", "l-r"); 409 break; 410 case SplitPane.ORIENTATION_HORIZONTAL_RIGHT_LEFT: 411 initElement.setAttribute("orientation", "r-l"); 412 break; 413 case SplitPane.ORIENTATION_VERTICAL_TOP_BOTTOM: 414 initElement.setAttribute("orientation", "t-b"); 415 break; 416 case SplitPane.ORIENTATION_VERTICAL_BOTTOM_TOP: 417 initElement.setAttribute("orientation", "b-t"); 418 break; 419 default: 420 throw new IllegalStateException ("Invalid orientation: " + orientation); 421 } 422 423 if (!splitPane.isRenderEnabled()) { 424 initElement.setAttribute("enabled", "false"); 425 } 426 Color background = (Color) splitPane.getRenderProperty(SplitPane.PROPERTY_BACKGROUND); 427 if (background != null) { 428 initElement.setAttribute("background", ColorRender.renderCssAttributeValue(background)); 429 } 430 Color foreground = (Color) splitPane.getRenderProperty(SplitPane.PROPERTY_FOREGROUND); 431 if (foreground != null) { 432 initElement.setAttribute("foreground", ColorRender.renderCssAttributeValue(foreground)); 433 } 434 Font font = (Font) splitPane.getRenderProperty(SplitPane.PROPERTY_FONT); 435 if (font != null) { 436 CssStyle fontCssStyle = new CssStyle(); 437 FontRender.renderToStyle(fontCssStyle, font); 438 initElement.setAttribute("font", fontCssStyle.renderInline()); 439 } 440 441 Boolean booleanValue = (Boolean ) splitPane.getRenderProperty(SplitPane.PROPERTY_RESIZABLE); 442 boolean resizable = booleanValue == null ? false : booleanValue.booleanValue(); 443 initElement.setAttribute("resizable", resizable ? "true" : "false"); 444 445 initElement.setAttribute("separator-size", Integer.toString(calculateSeparatorSize(splitPane))); 446 447 Color separatorColor = (Color) splitPane.getRenderProperty(SplitPane.PROPERTY_SEPARATOR_COLOR); 448 if (separatorColor != null) { 449 initElement.setAttribute("separator-color", ColorRender.renderCssAttributeValue(separatorColor)); 450 } 451 452 FillImage separatorImage = vertical 453 ? (FillImage) splitPane.getRenderProperty(SplitPane.PROPERTY_SEPARATOR_VERTICAL_IMAGE) 454 : (FillImage) splitPane.getRenderProperty(SplitPane.PROPERTY_SEPARATOR_HORIZONTAL_IMAGE); 455 if (separatorImage != null) { 456 CssStyle fillImageCssStyle = new CssStyle(); 457 FillImageRender.renderToStyle(fillImageCssStyle, rc, this, splitPane, 458 vertical ? IMAGE_ID_VERTICAL_SEPARATOR : IMAGE_ID_HORIZONTAL_SEPARATOR, separatorImage, 0); 459 initElement.setAttribute("separator-image", fillImageCssStyle.renderInline()); 460 } 461 462 Component[] children = splitPane.getVisibleComponents(); 463 for (int i = 0; i < children.length; ++i) { 464 renderLayoutData(rc, initElement, children[i], i); 465 } 466 467 partElement.appendChild(initElement); 468 } 469 470 private void renderLayoutData(RenderContext rc, Element containerElement, Component component, int index) { 471 SplitPaneLayoutData layoutData = (SplitPaneLayoutData) component.getRenderProperty(SplitPane.PROPERTY_LAYOUT_DATA); 472 if (layoutData == null) { 473 return; 474 } 475 Element layoutDataElement = rc.getServerMessage().getDocument().createElement("layout-data"); 476 layoutDataElement.setAttribute("index", Integer.toString(index)); 477 if (layoutData.getAlignment() != null) { 478 CssStyle alignmentStyle = new CssStyle(); 479 AlignmentRender.renderToStyle(alignmentStyle, layoutData.getAlignment(), component); 480 layoutDataElement.setAttribute("alignment", alignmentStyle.renderInline()); 481 } 482 if (layoutData.getBackground() != null) { 483 layoutDataElement.setAttribute("background", ColorRender.renderCssAttributeValue(layoutData.getBackground())); 484 } 485 if (layoutData.getBackgroundImage() != null) { 486 CssStyle backgroundImageStyle = new CssStyle(); 487 FillImageRender.renderToStyle(backgroundImageStyle, rc, this, component.getParent(), 488 index == 0 ? IMAGE_ID_PANE_0_BACKGROUND : IMAGE_ID_PANE_1_BACKGROUND, layoutData.getBackgroundImage(), 0); 489 layoutDataElement.setAttribute("background-image", backgroundImageStyle.renderInline()); 490 } 491 if (!(component instanceof Pane) && layoutData.getInsets() != null) { 492 layoutDataElement.setAttribute("insets", InsetsRender.renderCssAttributeValue(layoutData.getInsets())); 493 } 494 switch (layoutData.getOverflow()) { 495 case SplitPaneLayoutData.OVERFLOW_AUTO: 496 layoutDataElement.setAttribute("overflow", "auto"); 497 break; 498 case SplitPaneLayoutData.OVERFLOW_HIDDEN: 499 layoutDataElement.setAttribute("overflow", "hidden"); 500 break; 501 case SplitPaneLayoutData.OVERFLOW_SCROLL: 502 layoutDataElement.setAttribute("overflow", "scroll"); 503 break; 504 } 505 if (layoutData.getMinimumSize() != null) { 506 layoutDataElement.setAttribute("min-size", Integer.toString(ExtentRender.toPixels(layoutData.getMinimumSize(), -1))); 507 } 508 if (layoutData.getMaximumSize() != null) { 509 layoutDataElement.setAttribute("max-size", Integer.toString(ExtentRender.toPixels(layoutData.getMaximumSize(), -1))); 510 } 511 512 containerElement.appendChild(layoutDataElement); 513 } 514 515 private void renderRemoveChildren(RenderContext rc, ServerComponentUpdate update) { 516 ContainerInstance ci = rc.getContainerInstance(); 517 SplitPane splitPane = (SplitPane) update.getParent(); 518 RenderStateImpl previousRenderState = (RenderStateImpl) ci.getRenderState(splitPane); 519 520 RenderStateImpl currentRenderState = new RenderStateImpl(splitPane); 521 522 if (!equal(previousRenderState.pane0, currentRenderState.pane0)) { 523 if (previousRenderState.pane0 != null) { 524 renderRemoveChildDirective(rc, (SplitPane) splitPane, 0); 525 } 526 } 527 if (!equal(previousRenderState.pane1, currentRenderState.pane1)) { 528 if (previousRenderState.pane1 != null) { 529 renderRemoveChildDirective(rc, (SplitPane) splitPane, 1); 530 } 531 } 532 } 533 534 private void renderRemoveChildDirective(RenderContext rc, SplitPane splitPane, int index) { 535 String elementId = ContainerInstance.getElementId(splitPane); 536 ServerMessage serverMessage = rc.getServerMessage(); 537 Element partElement = serverMessage.addPart(ServerMessage.GROUP_ID_REMOVE, "EchoSplitPane.MessageProcessor"); 538 Element removeChildElement = serverMessage.getDocument().createElement("remove-child"); 539 removeChildElement.setAttribute("eid", elementId); 540 removeChildElement.setAttribute("index", Integer.toString(index)); 541 partElement.appendChild(removeChildElement); 542 } 543 544 private void renderSetSeparatorPositionDirective(RenderContext rc, SplitPane splitPane) { 545 String elementId = ContainerInstance.getElementId(splitPane); 546 ServerMessage serverMessage = rc.getServerMessage(); 547 Element partElement = serverMessage.addPart(ServerMessage.GROUP_ID_REMOVE, "EchoSplitPane.MessageProcessor"); 548 Element setSeparatorPositionElement = serverMessage.getDocument().createElement("set-separator-position"); 549 setSeparatorPositionElement.setAttribute("eid", elementId); 550 setSeparatorPositionElement.setAttribute("position", Integer.toString(getSeparatorPosition(splitPane))); 551 partElement.appendChild(setSeparatorPositionElement); 552 } 553 554 558 public boolean renderUpdate(RenderContext rc, ServerComponentUpdate update, String targetId) { 559 boolean fullReplace = false; 560 if (update.hasUpdatedLayoutDataChildren()) { 561 fullReplace = true; 562 } else if (update.hasUpdatedProperties()) { 563 if (!partialUpdateManager.canProcess(rc, update)) { 564 fullReplace = true; 565 } 566 } 567 568 if (fullReplace) { 569 renderDisposeDirective(rc, (SplitPane) update.getParent()); 571 DomUpdate.renderElementRemove(rc.getServerMessage(), ContainerInstance.getElementId(update.getParent())); 572 renderAdd(rc, update, targetId, update.getParent()); 573 } else { 574 partialUpdateManager.process(rc, update); 575 if (update.hasAddedChildren() || update.hasRemovedChildren()) { 576 renderRemoveChildren(rc, update); 577 renderAddChildren(rc, update); 578 } 579 } 580 581 updateRenderState(rc, (SplitPane) update.getParent()); 582 return fullReplace; 583 } 584 585 591 private void updateRenderState(RenderContext rc, SplitPane splitPane) { 592 rc.getContainerInstance().setRenderState(splitPane, new RenderStateImpl(splitPane)); 593 } 594 } 595 | Popular Tags |