1 29 30 package nextapp.echo2.webcontainer.syncpeer; 31 32 import java.util.HashMap ; 33 import java.util.Map ; 34 35 import org.w3c.dom.Document ; 36 import org.w3c.dom.Element ; 37 38 import nextapp.echo2.app.Border; 39 import nextapp.echo2.app.Color; 40 import nextapp.echo2.app.Component; 41 import nextapp.echo2.app.Extent; 42 import nextapp.echo2.app.Font; 43 import nextapp.echo2.app.Insets; 44 import nextapp.echo2.app.ListBox; 45 import nextapp.echo2.app.list.AbstractListComponent; 46 import nextapp.echo2.app.list.ListCellRenderer; 47 import nextapp.echo2.app.list.ListModel; 48 import nextapp.echo2.app.list.ListSelectionModel; 49 import nextapp.echo2.app.list.StyledListCell; 50 import nextapp.echo2.app.update.ServerComponentUpdate; 51 import nextapp.echo2.webcontainer.ActionProcessor; 52 import nextapp.echo2.webcontainer.ComponentSynchronizePeer; 53 import nextapp.echo2.webcontainer.ContainerInstance; 54 import nextapp.echo2.webcontainer.FocusSupport; 55 import nextapp.echo2.webcontainer.PartialUpdateManager; 56 import nextapp.echo2.webcontainer.PropertyUpdateProcessor; 57 import nextapp.echo2.webcontainer.RenderContext; 58 import nextapp.echo2.webcontainer.propertyrender.BorderRender; 59 import nextapp.echo2.webcontainer.propertyrender.ColorRender; 60 import nextapp.echo2.webcontainer.propertyrender.ExtentRender; 61 import nextapp.echo2.webcontainer.propertyrender.FontRender; 62 import nextapp.echo2.webcontainer.propertyrender.InsetsRender; 63 import nextapp.echo2.webrender.ClientProperties; 64 import nextapp.echo2.webrender.ServerMessage; 65 import nextapp.echo2.webrender.Service; 66 import nextapp.echo2.webrender.WebRenderServlet; 67 import nextapp.echo2.webrender.output.CssStyle; 68 import nextapp.echo2.webrender.servermessage.DomUpdate; 69 import nextapp.echo2.webrender.servermessage.WindowUpdate; 70 import nextapp.echo2.webrender.service.JavaScriptService; 71 import nextapp.echo2.webrender.util.DomUtil; 72 73 86 public class ListComponentPeer 87 implements ActionProcessor, ComponentSynchronizePeer, FocusSupport, PropertyUpdateProcessor { 88 89 92 public static final Service LIST_COMPONENT_SERVICE = JavaScriptService.forResource("Echo.ListComponent", 93 "/nextapp/echo2/webcontainer/resource/js/ListComponent.js"); 94 95 static { 96 WebRenderServlet.getServiceRegistry().add(LIST_COMPONENT_SERVICE); 97 } 98 99 private static final String PROPERTY_SELECTION = "selection"; 100 101 private static final Color DEFAULT_BACKGROUND = Color.WHITE; 103 private static final Color DEFAULT_FOREGROUND = Color.BLACK; 104 105 private static final Extent DEFAULT_WIDTH = new Extent(100, Extent.PERCENT); 107 private static final Insets DEFAULT_INSETS = new Insets(new Extent(0), new Extent(0)); 108 109 113 private static final String RENDERED_MODEL_MAP_KEY 114 = "nextapp.echo2.webcontainer.syncpeer.ListComponentPeer.RenderedModelMap"; 115 116 private PartialUpdateManager partialUpdateManager; 117 118 123 private class RenderedModelData { 124 125 128 private String [] values; 129 130 134 private String [] styles; 135 136 139 private int hashCode; 140 141 147 private RenderedModelData(AbstractListComponent listComponent) { 148 ListModel model = listComponent.getModel(); 149 ListCellRenderer renderer = listComponent.getCellRenderer(); 150 151 int size = model.size(); 152 values = new String [size]; 153 for (int i = 0; i < values.length; ++i) { 154 Object renderedValue = renderer.getListCellRendererComponent(listComponent, model.get(i), i); 155 values[i] = renderedValue.toString(); 156 157 if (renderedValue instanceof StyledListCell) { 158 StyledListCell styledListCell = (StyledListCell) renderedValue; 159 CssStyle itemStyle = new CssStyle(); 160 ColorRender.renderToStyle(itemStyle, styledListCell.getForeground(), styledListCell.getBackground()); 161 FontRender.renderToStyle(itemStyle, styledListCell.getFont()); 162 if (itemStyle.hasAttributes()) { 163 if (styles == null) { 164 styles = new String [size]; 165 } 166 styles[i] = itemStyle.renderInline(); 167 } 168 } 169 } 170 } 171 172 175 public int hashCode() { 176 if (hashCode == 0) { 177 hashCode = values.length; 178 for (int i = 0; i < values.length; ++i) { 179 if (values[i] != null) { 180 hashCode ^= values[i].hashCode(); 181 } 182 } 183 if (hashCode == 0) { 184 hashCode = 1; 185 } 186 } 187 return hashCode; 188 } 189 190 193 public boolean equals(Object o) { 194 if (!(o instanceof RenderedModelData)) { 195 return false; 196 } 197 RenderedModelData that = (RenderedModelData) o; 198 if (this.values.length != that.values.length) { 199 return false; 200 } 201 for (int i = 0; i < this.values.length; ++i) { 202 if (!(this.values[i] == that.values[i] || (this.values[i] != null && this.values[i].equals(that.values[i])))) { 203 return false; 204 } 205 } 206 if (this.styles != null || that.styles != null) { 207 if (this.styles == null || that.styles == null) { 208 return false; 209 } 210 for (int i = 0; i < this.styles.length; ++i) { 211 if (!(this.styles[i] == that.styles[i] || (this.styles[i] != null && this.styles[i].equals(that.styles[i])))) { 212 return false; 213 } 214 } 215 } 216 return true; 217 } 218 } 219 220 223 public ListComponentPeer() { 224 super(); 225 partialUpdateManager = new PartialUpdateManager(); 226 } 227 228 235 private CssStyle createListComponentCssStyle(RenderContext rc, AbstractListComponent listComponent) { 236 CssStyle cssStyle = new CssStyle(); 237 238 boolean renderEnabled = listComponent.isRenderEnabled(); 239 240 Border border; 241 Color foreground, background; 242 Font font; 243 if (!renderEnabled) { 244 background = (Color) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_DISABLED_BACKGROUND); 246 border = (Border) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_DISABLED_BORDER); 247 font = (Font) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_DISABLED_FONT); 248 foreground = (Color) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_DISABLED_FOREGROUND); 249 250 if (background == null) { 252 background = (Color) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_BACKGROUND, 253 DEFAULT_BACKGROUND); 254 } 255 if (border == null) { 256 border = (Border) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_BORDER); 257 } 258 if (font == null) { 259 font = (Font) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_FONT); 260 } 261 if (foreground == null) { 262 foreground = (Color) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_FOREGROUND, 263 DEFAULT_FOREGROUND); 264 } 265 } else { 266 border = (Border) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_BORDER); 267 foreground = (Color) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_FOREGROUND, DEFAULT_FOREGROUND); 268 background = (Color) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_BACKGROUND, DEFAULT_BACKGROUND); 269 font = (Font) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_FONT); 270 } 271 272 BorderRender.renderToStyle(cssStyle, border); 273 ColorRender.renderToStyle(cssStyle, foreground, background); 274 FontRender.renderToStyle(cssStyle, font); 275 276 Extent width = (Extent) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_WIDTH, DEFAULT_WIDTH); 277 if (rc.getContainerInstance().getClientProperties().getBoolean(ClientProperties.QUIRK_IE_SELECT_PERCENT_WIDTH) 278 && width.getUnits() == Extent.PERCENT) { 279 width = null; 281 } 282 Extent height = (Extent) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_HEIGHT); 283 Insets insets = (Insets) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_INSETS, DEFAULT_INSETS); 284 285 InsetsRender.renderToStyle(cssStyle, "padding", insets); 286 ExtentRender.renderToStyle(cssStyle, "width", width); 287 ExtentRender.renderToStyle(cssStyle, "height", height); 288 cssStyle.setAttribute("position", "relative"); 289 290 return cssStyle; 291 } 292 293 300 private CssStyle createRolloverCssStyle(AbstractListComponent listComponent) { 301 CssStyle style = new CssStyle(); 302 Color rolloverForeground = (Color) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_ROLLOVER_FOREGROUND); 303 Color rolloverBackground = (Color) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_ROLLOVER_BACKGROUND); 304 ColorRender.renderToStyle(style, rolloverForeground, rolloverBackground); 305 FontRender.renderToStyle(style, (Font) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_ROLLOVER_FONT)); 306 return style; 307 } 308 309 312 public String getContainerId(Component child) { 313 throw new UnsupportedOperationException ("Component does not support children."); 314 } 315 316 320 public void processAction(ContainerInstance ci, Component component, Element actionElement) { 321 ci.getUpdateManager().getClientUpdateManager().setComponentAction(component, AbstractListComponent.INPUT_ACTION, null); 322 } 323 324 328 public void processPropertyUpdate(ContainerInstance ci, Component component, Element propertyElement) { 329 String propertyName = propertyElement.getAttribute(PropertyUpdateProcessor.PROPERTY_NAME); 330 if (PROPERTY_SELECTION.equals(propertyName)) { 331 Element [] itemElements = DomUtil.getChildElementsByTagName(propertyElement, "item"); 332 int[] selectedIndices = new int[itemElements.length]; 333 for (int i = 0; i < itemElements.length; ++i) { 334 selectedIndices[i] = Integer.parseInt(itemElements[i].getAttribute("index")); 335 } 336 ci.getUpdateManager().getClientUpdateManager().setComponentProperty(component, 337 AbstractListComponent.SELECTION_CHANGED_PROPERTY, selectedIndices); 338 } 339 } 340 341 346 public void renderAdd(RenderContext rc, ServerComponentUpdate update, String targetId, Component component) { 347 ServerMessage serverMessage = rc.getServerMessage(); 348 serverMessage.addLibrary(LIST_COMPONENT_SERVICE.getId()); 349 AbstractListComponent listComponent = (AbstractListComponent) component; 350 renderInitDirective(rc, listComponent, targetId); 351 } 352 353 357 public void renderDispose(RenderContext rc, ServerComponentUpdate update, Component component) { 358 ServerMessage serverMessage = rc.getServerMessage(); 359 serverMessage.addLibrary(LIST_COMPONENT_SERVICE.getId()); 360 renderDisposeDirective(rc, (AbstractListComponent) component); 361 } 362 363 371 private void renderDisposeDirective(RenderContext rc, AbstractListComponent listComponent) { 372 String elementId = ContainerInstance.getElementId(listComponent); 373 ServerMessage serverMessage = rc.getServerMessage(); 374 Element initElement = serverMessage.appendPartDirective(ServerMessage.GROUP_ID_PREREMOVE, 375 "EchoListComponent.MessageProcessor", "dispose"); 376 initElement.setAttribute("eid", elementId); 377 } 378 379 387 private String renderContent(RenderContext rc, AbstractListComponent listComponent) { 388 RenderedModelData renderedModelData = new RenderedModelData(listComponent); 389 390 Map renderedModelDataToIdMap = (Map ) rc.getConnection().getProperty(RENDERED_MODEL_MAP_KEY); 391 if (renderedModelDataToIdMap == null) { 392 renderedModelDataToIdMap = new HashMap (); 393 rc.getConnection().setProperty(RENDERED_MODEL_MAP_KEY, renderedModelDataToIdMap); 394 } 395 String contentId = (String ) renderedModelDataToIdMap.get(renderedModelData); 396 if (contentId == null) { 397 contentId = Integer.toString(renderedModelDataToIdMap.size()); 398 renderedModelDataToIdMap.put(renderedModelData, contentId); 399 renderLoadContentDirective(rc, renderedModelData, contentId); 400 } 401 return contentId; 402 } 403 404 412 private void renderLoadContentDirective(RenderContext rc, RenderedModelData renderedModelData, String contentId) { 413 ServerMessage serverMessage = rc.getServerMessage(); 414 Element partElement = serverMessage.appendPartDirective(ServerMessage.GROUP_ID_INIT, 415 "EchoListComponent.MessageProcessor", "load-content"); 416 partElement.setAttribute("content-id", contentId); 417 Document document = serverMessage.getDocument(); 418 419 if (renderedModelData.styles != null) { 420 partElement.setAttribute("styled", "true"); 421 } 422 423 for (int i = 0; i < renderedModelData.values.length; ++i) { 424 Element itemElement = document.createElement("item"); 425 itemElement.setAttribute("value", renderedModelData.values[i] == null 426 ? "" : renderedModelData.values[i].toString()); 427 if (renderedModelData.styles != null) { 428 itemElement.setAttribute("style", renderedModelData.styles[i] == null 429 ? "" : renderedModelData.styles[i].toString()); 430 } 431 432 partElement.appendChild(itemElement); 433 } 434 } 435 436 444 private void renderInitDirective(RenderContext rc, AbstractListComponent listComponent, String targetId) { 445 String contentId = renderContent(rc, listComponent); 446 String elementId = ContainerInstance.getElementId(listComponent); 447 ServerMessage serverMessage = rc.getServerMessage(); 448 Document document = serverMessage.getDocument(); 449 450 Element initElement = serverMessage.appendPartDirective(ServerMessage.GROUP_ID_UPDATE, 451 "EchoListComponent.MessageProcessor", "init"); 452 initElement.setAttribute("container-eid", targetId); 453 initElement.setAttribute("eid", elementId); 454 initElement.setAttribute("content-id", contentId); 455 456 initElement.setAttribute("enabled", listComponent.isRenderEnabled() ? "true" : "false"); 457 458 if (listComponent.hasActionListeners()) { 459 initElement.setAttribute("server-notify", "true"); 460 } 461 462 CssStyle cssStyle = createListComponentCssStyle(rc, listComponent); 463 initElement.setAttribute("style", cssStyle.renderInline()); 464 465 Boolean rolloverEnabled = (Boolean ) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_ROLLOVER_ENABLED); 466 if (Boolean.TRUE.equals(rolloverEnabled)) { 467 CssStyle rolloverCssStyle = createRolloverCssStyle(listComponent); 468 initElement.setAttribute("rollover-style", rolloverCssStyle.renderInline()); 469 } 470 471 boolean multipleSelect = false; 472 if (listComponent instanceof ListBox) { 473 initElement.setAttribute("type", "list-box"); 474 ListBox listBox = (ListBox) listComponent; 475 if (listBox.getSelectionMode() == ListSelectionModel.MULTIPLE_SELECTION) { 476 initElement.setAttribute("multiple", "true"); 477 multipleSelect = true; 478 } 479 } 480 481 if (listComponent.isFocusTraversalParticipant()) { 482 initElement.setAttribute("tab-index", Integer.toString(listComponent.getFocusTraversalIndex())); 483 } else { 484 initElement.setAttribute("tab-index", "-1"); 485 } 486 487 String toolTipText = (String ) listComponent.getRenderProperty(AbstractListComponent.PROPERTY_TOOL_TIP_TEXT); 488 if (toolTipText != null) { 489 initElement.setAttribute("tool-tip", toolTipText); 490 } 491 492 ListSelectionModel selectionModel = listComponent.getSelectionModel(); 494 int minIndex = selectionModel.getMinSelectedIndex(); 495 if (minIndex >= 0) { 496 if (multipleSelect) { 497 Element selectionElement = document.createElement("selection"); 498 int maxIndex = selectionModel.getMaxSelectedIndex(); 499 for (int i = minIndex; i <= maxIndex; ++i) { 500 if (selectionModel.isSelectedIndex(i)) { 501 Element itemElement = document.createElement("item"); 502 itemElement.setAttribute("index", Integer.toString(i)); 503 selectionElement.appendChild(itemElement); 504 } 505 } 506 initElement.appendChild(selectionElement); 507 } else { 508 initElement.setAttribute("selection-index", Integer.toString(minIndex)); 509 } 510 } 511 } 512 513 516 public void renderSetFocus(RenderContext rc, Component component) { 517 if (component.isEnabled()) { 518 WindowUpdate.renderSetFocus(rc.getServerMessage(), ContainerInstance.getElementId(component)); 519 } 520 } 521 522 526 public boolean renderUpdate(RenderContext rc, ServerComponentUpdate update, String targetId) { 527 if (partialUpdateManager.canProcess(rc, update)) { 529 partialUpdateManager.process(rc, update); 530 } else { 531 DomUpdate.renderElementRemove(rc.getServerMessage(), ContainerInstance.getElementId(update.getParent())); 533 renderAdd(rc, update, targetId, update.getParent()); 534 } 535 536 return true; 537 } 538 } 539 | Popular Tags |