0001: /*
0002: * This file is part of the Echo Web Application Framework (hereinafter "Echo").
0003: * Copyright (C) 2002-2005 NextApp, Inc.
0004: *
0005: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
0006: *
0007: * The contents of this file are subject to the Mozilla Public License Version
0008: * 1.1 (the "License"); you may not use this file except in compliance with
0009: * the License. You may obtain a copy of the License at
0010: * http://www.mozilla.org/MPL/
0011: *
0012: * Software distributed under the License is distributed on an "AS IS" basis,
0013: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0014: * for the specific language governing rights and limitations under the
0015: * License.
0016: *
0017: * Alternatively, the contents of this file may be used under the terms of
0018: * either the GNU General Public License Version 2 or later (the "GPL"), or
0019: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
0020: * in which case the provisions of the GPL or the LGPL are applicable instead
0021: * of those above. If you wish to allow use of your version of this file only
0022: * under the terms of either the GPL or the LGPL, and not to allow others to
0023: * use your version of this file under the terms of the MPL, indicate your
0024: * decision by deleting the provisions above and replace them with the notice
0025: * and other provisions required by the GPL or the LGPL. If you do not delete
0026: * the provisions above, a recipient may use your version of this file under
0027: * the terms of any one of the MPL, the GPL or the LGPL.
0028: */
0029:
0030: package nextapp.echo2.webcontainer.syncpeer;
0031:
0032: import nextapp.echo2.app.Alignment;
0033: import nextapp.echo2.app.Border;
0034: import nextapp.echo2.app.CheckBox;
0035: import nextapp.echo2.app.Color;
0036: import nextapp.echo2.app.Component;
0037: import nextapp.echo2.app.Extent;
0038: import nextapp.echo2.app.FillImage;
0039: import nextapp.echo2.app.Font;
0040: import nextapp.echo2.app.ImageReference;
0041: import nextapp.echo2.app.Insets;
0042: import nextapp.echo2.app.RadioButton;
0043: import nextapp.echo2.app.ResourceImageReference;
0044: import nextapp.echo2.app.button.AbstractButton;
0045: import nextapp.echo2.app.button.ButtonGroup;
0046: import nextapp.echo2.app.button.ToggleButton;
0047: import nextapp.echo2.app.update.ServerComponentUpdate;
0048: import nextapp.echo2.webcontainer.ActionProcessor;
0049: import nextapp.echo2.webcontainer.ContainerInstance;
0050: import nextapp.echo2.webcontainer.DomUpdateSupport;
0051: import nextapp.echo2.webcontainer.PropertyUpdateProcessor;
0052: import nextapp.echo2.webcontainer.RenderContext;
0053: import nextapp.echo2.webcontainer.ComponentSynchronizePeer;
0054: import nextapp.echo2.webcontainer.image.ImageRenderSupport;
0055: import nextapp.echo2.webcontainer.image.ImageTools;
0056: import nextapp.echo2.webcontainer.propertyrender.AlignmentRender;
0057: import nextapp.echo2.webcontainer.propertyrender.BorderRender;
0058: import nextapp.echo2.webcontainer.propertyrender.ColorRender;
0059: import nextapp.echo2.webcontainer.propertyrender.ExtentRender;
0060: import nextapp.echo2.webcontainer.propertyrender.FillImageRender;
0061: import nextapp.echo2.webcontainer.propertyrender.FontRender;
0062: import nextapp.echo2.webcontainer.propertyrender.ImageReferenceRender;
0063: import nextapp.echo2.webcontainer.propertyrender.InsetsRender;
0064: import nextapp.echo2.webcontainer.propertyrender.LayoutDirectionRender;
0065: import nextapp.echo2.webrender.ServerMessage;
0066: import nextapp.echo2.webrender.Service;
0067: import nextapp.echo2.webrender.WebRenderServlet;
0068: import nextapp.echo2.webrender.output.CssStyle;
0069: import nextapp.echo2.webrender.servermessage.DomUpdate;
0070: import nextapp.echo2.webrender.service.JavaScriptService;
0071:
0072: import org.w3c.dom.Document;
0073: import org.w3c.dom.DocumentFragment;
0074: import org.w3c.dom.Element;
0075: import org.w3c.dom.Node;
0076: import org.w3c.dom.Text;
0077:
0078: /**
0079: * Synchronization peer for
0080: * <code>nextapp.echo2.app.AbstractButton</code>-derived components.
0081: * <p>
0082: * This class should not be extended or used by classes outside of the
0083: * Echo framework.
0084: */
0085: public class ButtonPeer implements ActionProcessor, DomUpdateSupport,
0086: ImageRenderSupport, PropertyUpdateProcessor,
0087: ComponentSynchronizePeer {
0088:
0089: private static final Alignment DEFAULT_TEXT_POSITION = new Alignment(
0090: Alignment.TRAILING, Alignment.DEFAULT);
0091: private static final Alignment DEFAULT_STATE_POSITION = new Alignment(
0092: Alignment.LEADING, Alignment.DEFAULT);
0093: private static final Extent DEFAULT_ICON_TEXT_MARGIN = new Extent(5);
0094: private static final ImageReference DEFAULT_CHECKBOX_ICON = new ResourceImageReference(
0095: "/nextapp/echo2/webcontainer/resource/image/CheckBoxOff.gif");
0096: private static final ImageReference DEFAULT_SELECTED_CHECKBOX_ICON = new ResourceImageReference(
0097: "/nextapp/echo2/webcontainer/resource/image/CheckBoxOn.gif");
0098: private static final ImageReference DEFAULT_RADIOBUTTON_ICON = new ResourceImageReference(
0099: "/nextapp/echo2/webcontainer/resource/image/RadioButtonOff.gif");
0100: private static final ImageReference DEFAULT_SELECTED_RADIOBUTTON_ICON = new ResourceImageReference(
0101: "/nextapp/echo2/webcontainer/resource/image/RadioButtonOn.gif");
0102:
0103: private static final String[] BUTTON_INIT_KEYS = new String[] {
0104: "default-style", "rollover-style", "pressed-style" };
0105:
0106: private static final String IMAGE_ID_BACKGROUND = "background";
0107: private static final String IMAGE_ID_ICON = "icon";
0108: private static final String IMAGE_ID_ROLLOVER_BACKGROUND = "rolloverBackground";
0109: private static final String IMAGE_ID_ROLLOVER_ICON = "rolloverIcon";
0110: private static final String IMAGE_ID_ROLLOVER_STATE_ICON = "rolloverStateIcon";
0111: private static final String IMAGE_ID_ROLLOVER_SELECTED_STATE_ICON = "rolloverSelectedStateIcon";
0112: private static final String IMAGE_ID_PRESSED_BACKGROUND = "pressedBackground";
0113: private static final String IMAGE_ID_PRESSED_ICON = "pressedIcon";
0114: private static final String IMAGE_ID_PRESSED_STATE_ICON = "pressedStateIcon";
0115: private static final String IMAGE_ID_PRESSED_SELECTED_STATE_ICON = "pressedSelectedStateIcon";
0116: private static final String IMAGE_ID_STATE_ICON = "stateIcon";
0117: private static final String IMAGE_ID_SELECTED_STATE_ICON = "selectedStateIcon";
0118:
0119: private static final String CONTAINER_TABLE_CSS_TEXT_DEFAULT = "border:0px none;border-collapse:collapse;";
0120: private static final String CONTAINER_TABLE_CSS_TEXT_LEFT = "border:0px none;border-collapse:collapse; margin: 0 auto 0 0";
0121: private static final String CONTAINER_TABLE_CSS_TEXT_CENTER = "border:0px none;border-collapse:collapse; margin: 0 auto;";
0122: private static final String CONTAINER_TABLE_CSS_TEXT_RIGHT = "border:0px none;border-collapse:collapse; margin: 0 0 0 auto;";
0123:
0124: /**
0125: * Service to provide supporting JavaScript library.
0126: */
0127: private static final Service BUTTON_SERVICE = JavaScriptService
0128: .forResource("Echo.Button",
0129: "/nextapp/echo2/webcontainer/resource/js/Button.js");
0130:
0131: static {
0132: WebRenderServlet.getServiceRegistry().add(BUTTON_SERVICE);
0133: }
0134:
0135: /**
0136: * Determines the CSS text which should be placed in the 'style' attribute
0137: * of the button's container TABLE element.
0138: *
0139: * @param button the rendering <code>AbstractButton</code>
0140: * @return the CSS text
0141: */
0142: private static String getContainerTableCssText(AbstractButton button) {
0143: Alignment alignment = (Alignment) button
0144: .getRenderProperty(AbstractButton.PROPERTY_ALIGNMENT);
0145: if (alignment != null) {
0146: int horizontal = AlignmentRender.getRenderedHorizontal(
0147: alignment, button);
0148: switch (horizontal) {
0149: case Alignment.LEFT:
0150: return CONTAINER_TABLE_CSS_TEXT_LEFT;
0151: case Alignment.CENTER:
0152: return CONTAINER_TABLE_CSS_TEXT_CENTER;
0153: case Alignment.RIGHT:
0154: return CONTAINER_TABLE_CSS_TEXT_RIGHT;
0155: }
0156: }
0157: return CONTAINER_TABLE_CSS_TEXT_DEFAULT;
0158: }
0159:
0160: /**
0161: * @see nextapp.echo2.webcontainer.ComponentSynchronizePeer#getContainerId(nextapp.echo2.app.Component)
0162: */
0163: public String getContainerId(Component child) {
0164: throw new UnsupportedOperationException(
0165: "Component does not support children.");
0166: }
0167:
0168: /**
0169: * Combines the properties of two <code>Alignment</code> objects together.
0170: * Properties of the <code>secondary</code> object will override default
0171: * properties of the <code>primary</code>.
0172: *
0173: * @param primary the first <code>Alignment</code> (may be null)
0174: * @param secondary the second <code>Alignment</code> (may be null)
0175: * @return a new <code>Alignment</code> combining the values of both (or
0176: * null if both input <code>Alignment</code>s were null
0177: */
0178: private Alignment combineAlignment(Alignment primary,
0179: Alignment secondary) {
0180: if (primary == null) {
0181: return secondary;
0182: } else if (secondary == null) {
0183: return primary;
0184: }
0185: int horizontal = primary.getHorizontal();
0186: int vertical = primary.getVertical();
0187: Alignment alignment = new Alignment(
0188: horizontal == Alignment.DEFAULT ? secondary
0189: .getHorizontal() : horizontal,
0190: vertical == Alignment.DEFAULT ? secondary.getVertical()
0191: : vertical);
0192: return alignment;
0193: }
0194:
0195: /**
0196: * @see nextapp.echo2.webcontainer.image.ImageRenderSupport#getImage(nextapp.echo2.app.Component,
0197: * java.lang.String)
0198: */
0199: public ImageReference getImage(Component component, String imageId) {
0200: if (IMAGE_ID_ICON.equals(imageId)) {
0201: if (component.isRenderEnabled()) {
0202: return (ImageReference) component
0203: .getRenderProperty(AbstractButton.PROPERTY_ICON);
0204: } else {
0205: ImageReference icon = (ImageReference) component
0206: .getRenderProperty(AbstractButton.PROPERTY_DISABLED_ICON);
0207: if (icon == null) {
0208: icon = (ImageReference) component
0209: .getRenderProperty(AbstractButton.PROPERTY_ICON);
0210: }
0211: return icon;
0212: }
0213: } else if (IMAGE_ID_ROLLOVER_ICON.equals(imageId)) {
0214: return (ImageReference) component
0215: .getRenderProperty(AbstractButton.PROPERTY_ROLLOVER_ICON);
0216: } else if (IMAGE_ID_PRESSED_ICON.equals(imageId)) {
0217: return (ImageReference) component
0218: .getRenderProperty(AbstractButton.PROPERTY_PRESSED_ICON);
0219: } else if (IMAGE_ID_STATE_ICON.equals(imageId)) {
0220: return getStateIcon((ToggleButton) component);
0221: } else if (IMAGE_ID_SELECTED_STATE_ICON.equals(imageId)) {
0222: return getSelectedStateIcon((ToggleButton) component);
0223: } else if (IMAGE_ID_BACKGROUND.equals(imageId)) {
0224: FillImage backgroundImage;
0225: if (component.isRenderEnabled()) {
0226: backgroundImage = (FillImage) component
0227: .getRenderProperty(AbstractButton.PROPERTY_BACKGROUND_IMAGE);
0228: } else {
0229: backgroundImage = (FillImage) component
0230: .getRenderProperty(AbstractButton.PROPERTY_DISABLED_BACKGROUND_IMAGE);
0231: if (backgroundImage == null) {
0232: backgroundImage = (FillImage) component
0233: .getRenderProperty(AbstractButton.PROPERTY_BACKGROUND_IMAGE);
0234: }
0235: }
0236: if (backgroundImage == null) {
0237: return null;
0238: } else {
0239: return backgroundImage.getImage();
0240: }
0241: } else if (IMAGE_ID_ROLLOVER_BACKGROUND.equals(imageId)) {
0242: FillImage backgroundImage = (FillImage) component
0243: .getRenderProperty(AbstractButton.PROPERTY_ROLLOVER_BACKGROUND_IMAGE);
0244: if (backgroundImage == null) {
0245: return null;
0246: } else {
0247: return backgroundImage.getImage();
0248: }
0249: } else if (IMAGE_ID_PRESSED_BACKGROUND.equals(imageId)) {
0250: FillImage backgroundImage = (FillImage) component
0251: .getRenderProperty(AbstractButton.PROPERTY_PRESSED_BACKGROUND_IMAGE);
0252: if (backgroundImage == null) {
0253: return null;
0254: } else {
0255: return backgroundImage.getImage();
0256: }
0257: } else if (IMAGE_ID_ROLLOVER_STATE_ICON.equals(imageId)) {
0258: ImageReference icon = (ImageReference) component
0259: .getRenderProperty(ToggleButton.PROPERTY_ROLLOVER_STATE_ICON);
0260: return icon == null ? getStateIcon((ToggleButton) component)
0261: : icon;
0262: } else if (IMAGE_ID_ROLLOVER_SELECTED_STATE_ICON
0263: .equals(imageId)) {
0264: ImageReference icon = (ImageReference) component
0265: .getRenderProperty(ToggleButton.PROPERTY_ROLLOVER_SELECTED_STATE_ICON);
0266: return icon == null ? getSelectedStateIcon((ToggleButton) component)
0267: : icon;
0268: } else if (IMAGE_ID_PRESSED_STATE_ICON.equals(imageId)) {
0269: ImageReference icon = (ImageReference) component
0270: .getRenderProperty(ToggleButton.PROPERTY_PRESSED_STATE_ICON);
0271: return icon == null ? getStateIcon((ToggleButton) component)
0272: : icon;
0273: } else if (IMAGE_ID_PRESSED_SELECTED_STATE_ICON.equals(imageId)) {
0274: ImageReference icon = (ImageReference) component
0275: .getRenderProperty(ToggleButton.PROPERTY_PRESSED_SELECTED_STATE_ICON);
0276: return icon == null ? getSelectedStateIcon((ToggleButton) component)
0277: : icon;
0278: } else {
0279: return null;
0280: }
0281: }
0282:
0283: /**
0284: * Determines the selected state icon of the specified
0285: * <code>ToggleButton</code>.
0286: *
0287: * @param toggleButton the <code>ToggleButton</code>
0288: * @return the selected state icon
0289: */
0290: private ImageReference getSelectedStateIcon(
0291: ToggleButton toggleButton) {
0292: ImageReference selectedStateIcon = (ImageReference) toggleButton
0293: .getRenderProperty(ToggleButton.PROPERTY_SELECTED_STATE_ICON);
0294: if (selectedStateIcon == null) {
0295: if (toggleButton instanceof CheckBox) {
0296: selectedStateIcon = DEFAULT_SELECTED_CHECKBOX_ICON;
0297: } else if (toggleButton instanceof RadioButton) {
0298: selectedStateIcon = DEFAULT_SELECTED_RADIOBUTTON_ICON;
0299: }
0300: }
0301: return selectedStateIcon;
0302: }
0303:
0304: /**
0305: * Determines the default (non-selected) state icon of the specified
0306: * <code>ToggleButton</code>.
0307: *
0308: * @param toggleButton the <code>ToggleButton</code>
0309: * @return the state icon
0310: */
0311: private ImageReference getStateIcon(ToggleButton toggleButton) {
0312: ImageReference stateIcon = (ImageReference) toggleButton
0313: .getRenderProperty(ToggleButton.PROPERTY_STATE_ICON);
0314: if (stateIcon == null) {
0315: if (toggleButton instanceof CheckBox) {
0316: stateIcon = DEFAULT_CHECKBOX_ICON;
0317: } else if (toggleButton instanceof RadioButton) {
0318: stateIcon = DEFAULT_RADIOBUTTON_ICON;
0319: }
0320: }
0321: return stateIcon;
0322: }
0323:
0324: /**
0325: * @see nextapp.echo2.webcontainer.ActionProcessor#processAction(nextapp.echo2.webcontainer.ContainerInstance,
0326: * nextapp.echo2.app.Component, org.w3c.dom.Element)
0327: */
0328: public void processAction(ContainerInstance ci,
0329: Component component, Element actionElement) {
0330: ci.getUpdateManager().getClientUpdateManager()
0331: .setComponentAction(component,
0332: AbstractButton.INPUT_CLICK, null);
0333: }
0334:
0335: /**
0336: * @see nextapp.echo2.webcontainer.PropertyUpdateProcessor#processPropertyUpdate(
0337: * nextapp.echo2.webcontainer.ContainerInstance, nextapp.echo2.app.Component, org.w3c.dom.Element)
0338: */
0339: public void processPropertyUpdate(ContainerInstance ci,
0340: Component component, Element propertyElement) {
0341:
0342: String propertyName = propertyElement
0343: .getAttribute(PropertyUpdateProcessor.PROPERTY_NAME);
0344: if (ToggleButton.SELECTED_CHANGED_PROPERTY.equals(propertyName)) {
0345: Boolean propertyValue = new Boolean("true"
0346: .equals(propertyElement.getAttribute("value")));
0347: ci.getUpdateManager().getClientUpdateManager()
0348: .setComponentProperty(component,
0349: ToggleButton.SELECTED_CHANGED_PROPERTY,
0350: propertyValue);
0351: }
0352: }
0353:
0354: /**
0355: * @see nextapp.echo2.webcontainer.ComponentSynchronizePeer#renderAdd(nextapp.echo2.webcontainer.RenderContext,
0356: * nextapp.echo2.app.update.ServerComponentUpdate, java.lang.String, nextapp.echo2.app.Component)
0357: */
0358: public void renderAdd(RenderContext rc,
0359: ServerComponentUpdate update, String targetId,
0360: Component component) {
0361: Element domAddElement = DomUpdate.renderElementAdd(rc
0362: .getServerMessage());
0363: DocumentFragment htmlFragment = rc.getServerMessage()
0364: .getDocument().createDocumentFragment();
0365: renderHtml(rc, update, htmlFragment, component);
0366: DomUpdate.renderElementAddContent(rc.getServerMessage(),
0367: domAddElement, targetId, htmlFragment);
0368: }
0369:
0370: /**
0371: * Renders the containing DIV element of a button.
0372: *
0373: * @param rc the relevant <code>RenderContext</code>
0374: * @param parentNode the parent node
0375: * @param button the <code>AbstractButton</code> being rendered
0376: * @return the rendered DIV element (note that this element will already
0377: * have been appended to the parent)
0378: */
0379: private Element renderButtonContainer(RenderContext rc,
0380: Node parentNode, AbstractButton button) {
0381: Element divElement = parentNode.getOwnerDocument()
0382: .createElement("div");
0383: divElement.setAttribute("id", ContainerInstance
0384: .getElementId(button));
0385:
0386: if (button.isFocusTraversalParticipant()) {
0387: divElement.setAttribute("tabindex", Integer.toString(button
0388: .getFocusTraversalIndex()));
0389: } else {
0390: divElement.setAttribute("tabindex", "-1");
0391: }
0392:
0393: boolean renderEnabled = button.isRenderEnabled();
0394:
0395: String toolTipText = (String) button
0396: .getRenderProperty(AbstractButton.PROPERTY_TOOL_TIP_TEXT);
0397: if (renderEnabled && toolTipText != null) {
0398: divElement.setAttribute("title", toolTipText);
0399: }
0400: divElement.setAttribute("style", "visibility:hidden;");
0401:
0402: parentNode.appendChild(divElement);
0403: return divElement;
0404: }
0405:
0406: /**
0407: * Renders the content of the button, i.e., its text, icon, and/or state icon.
0408: *
0409: * @param rc the relevant <code>RenderContext</code>
0410: * @param buttonContainerElement the <code>Element</code> which will
0411: * contain the content
0412: * @param button the <code>AbstractButton</code> being rendered
0413: */
0414: private void renderButtonContent(RenderContext rc,
0415: Element buttonContainerElement, AbstractButton button) {
0416: Node contentNode;
0417: Document document = rc.getServerMessage().getDocument();
0418: ToggleButton toggleButton = button instanceof ToggleButton ? (ToggleButton) button
0419: : null;
0420: String elementId = ContainerInstance.getElementId(button);
0421:
0422: String text = (String) button
0423: .getRenderProperty(AbstractButton.PROPERTY_TEXT);
0424: ImageReference icon = (ImageReference) button
0425: .getRenderProperty(AbstractButton.PROPERTY_ICON);
0426:
0427: // Create entities.
0428: Text textNode = text == null ? null
0429: : rc
0430: .getServerMessage()
0431: .getDocument()
0432: .createTextNode(
0433: (String) button
0434: .getRenderProperty(AbstractButton.PROPERTY_TEXT));
0435:
0436: Element iconElement;
0437: if (icon == null) {
0438: iconElement = null;
0439: } else {
0440: iconElement = ImageReferenceRender
0441: .renderImageReferenceElement(rc, ButtonPeer.this ,
0442: button, IMAGE_ID_ICON);
0443: iconElement.setAttribute("id", elementId + "_icon");
0444: }
0445:
0446: Element stateIconElement;
0447: if (toggleButton == null) {
0448: stateIconElement = null;
0449: } else {
0450: stateIconElement = ImageReferenceRender
0451: .renderImageReferenceElement(
0452: rc,
0453: ButtonPeer.this ,
0454: button,
0455: toggleButton.isSelected() ? IMAGE_ID_SELECTED_STATE_ICON
0456: : IMAGE_ID_STATE_ICON);
0457: stateIconElement.setAttribute("id", elementId
0458: + "_stateicon");
0459: }
0460:
0461: int entityCount = (textNode == null ? 0 : 1)
0462: + (iconElement == null ? 0 : 1)
0463: + (stateIconElement == null ? 0 : 1);
0464:
0465: Extent iconTextMargin;
0466: Alignment textPosition;
0467: Element tableElement;
0468:
0469: switch (entityCount) {
0470: case 1:
0471: if (textNode != null) {
0472: contentNode = textNode;
0473: } else if (iconElement != null) {
0474: contentNode = iconElement;
0475: } else { // stateIconElement must not be null.
0476: contentNode = stateIconElement;
0477: }
0478: break;
0479: case 2:
0480: iconTextMargin = (Extent) button.getRenderProperty(
0481: AbstractButton.PROPERTY_ICON_TEXT_MARGIN,
0482: DEFAULT_ICON_TEXT_MARGIN);
0483: TriCellTable tct;
0484: textPosition = (Alignment) button.getRenderProperty(
0485: AbstractButton.PROPERTY_TEXT_POSITION,
0486: DEFAULT_TEXT_POSITION);
0487: if (stateIconElement == null) {
0488: // Not rendering a ToggleButton.
0489: int orientation = TriCellTableConfigurator
0490: .convertIconTextPositionToOrientation(
0491: textPosition, button);
0492: tct = new TriCellTable(rc, document, elementId,
0493: orientation, iconTextMargin);
0494:
0495: renderCellText(tct, textNode, button);
0496: renderCellIcon(tct, iconElement, 1, button);
0497: } else {
0498: // Rendering a ToggleButton.
0499: Extent stateMargin = (Extent) button.getRenderProperty(
0500: ToggleButton.PROPERTY_STATE_MARGIN,
0501: DEFAULT_ICON_TEXT_MARGIN);
0502: Alignment statePosition = (Alignment) button
0503: .getRenderProperty(
0504: ToggleButton.PROPERTY_STATE_POSITION,
0505: DEFAULT_STATE_POSITION);
0506: int orientation = TriCellTableConfigurator
0507: .convertStatePositionToOrientation(
0508: statePosition, button);
0509: tct = new TriCellTable(rc, document, elementId,
0510: orientation, stateMargin);
0511:
0512: if (textNode == null) {
0513: renderCellIcon(tct, iconElement, 0, button);
0514: } else {
0515: renderCellText(tct, textNode, button);
0516: }
0517: renderCellState(tct, stateIconElement, 1, button);
0518: }
0519:
0520: tct.addCellCssText("padding:0px;");
0521: tableElement = tct.getTableElement();
0522: tableElement.setAttribute("id", elementId + "_table");
0523: tableElement.setAttribute("style",
0524: getContainerTableCssText(button));
0525: contentNode = tableElement;
0526: break;
0527: case 3:
0528: iconTextMargin = (Extent) button.getRenderProperty(
0529: AbstractButton.PROPERTY_ICON_TEXT_MARGIN,
0530: DEFAULT_ICON_TEXT_MARGIN);
0531: textPosition = (Alignment) button.getRenderProperty(
0532: AbstractButton.PROPERTY_TEXT_POSITION,
0533: DEFAULT_TEXT_POSITION);
0534: Extent stateMargin = (Extent) button.getRenderProperty(
0535: ToggleButton.PROPERTY_STATE_MARGIN,
0536: DEFAULT_ICON_TEXT_MARGIN);
0537: Alignment statePosition = (Alignment) button
0538: .getRenderProperty(
0539: ToggleButton.PROPERTY_STATE_POSITION,
0540: DEFAULT_STATE_POSITION);
0541: int stateOrientation = TriCellTableConfigurator
0542: .convertStatePositionToOrientation(statePosition,
0543: button);
0544: int orientation = TriCellTableConfigurator
0545: .convertIconTextPositionToOrientation(textPosition,
0546: button);
0547: tct = new TriCellTable(rc, document, elementId,
0548: orientation, iconTextMargin, stateOrientation,
0549: stateMargin);
0550:
0551: renderCellText(tct, textNode, button);
0552: renderCellIcon(tct, iconElement, 1, button);
0553: renderCellState(tct, stateIconElement, 2, button);
0554:
0555: tct.addCellCssText("padding:0px;");
0556: tableElement = tct.getTableElement();
0557: tableElement.setAttribute("id", elementId + "_table");
0558: tableElement.setAttribute("style",
0559: getContainerTableCssText(button));
0560: contentNode = tableElement;
0561: break;
0562: default:
0563: // 0 element button.
0564: contentNode = null;
0565: }
0566:
0567: if (contentNode != null) {
0568: buttonContainerElement.appendChild(contentNode);
0569: }
0570: }
0571:
0572: /**
0573: * Renders the content of the <code>TriCellTable</code> cell which
0574: * contains the button's icon.
0575: *
0576: * @param tct the <code>TriCellTable</code> to update
0577: * @param iconElement the icon element
0578: * @param cellIndex the index of the cell in the <code>TriCellTable</code>
0579: * that should contain the icon
0580: */
0581: private void renderCellIcon(TriCellTable tct, Element iconElement,
0582: int cellIndex, AbstractButton button) {
0583: Element iconTdElement = tct.getTdElement(cellIndex);
0584: Alignment alignment = (Alignment) button
0585: .getRenderProperty(AbstractButton.PROPERTY_ALIGNMENT);
0586: if (alignment != null) {
0587: CssStyle style = new CssStyle();
0588: AlignmentRender.renderToStyle(style, alignment, button);
0589: iconTdElement.setAttribute("style", style.renderInline());
0590: }
0591: iconTdElement.appendChild(iconElement);
0592: }
0593:
0594: /**
0595: * Renders the content of the <code>TriCellTable</code> cell which
0596: * contains the button's state icon.
0597: *
0598: * @param tct the <code>TriCellTable</code> to update
0599: * @param stateIconElement the state icon element
0600: * @param cellIndex the index of the cell in the <code>TriCellTable</code>
0601: * that should contain the state icon
0602: * @param button the <code>AbstractButton</code> being rendered
0603: */
0604: private void renderCellState(TriCellTable tct,
0605: Element stateIconElement, int cellIndex,
0606: AbstractButton button) {
0607: Element stateTdElement = tct.getTdElement(cellIndex);
0608: CssStyle stateTdCssStyle = new CssStyle();
0609: AlignmentRender
0610: .renderToStyle(
0611: stateTdCssStyle,
0612: (Alignment) button
0613: .getRenderProperty(ToggleButton.PROPERTY_STATE_ALIGNMENT),
0614: button);
0615: stateTdElement.setAttribute("style", stateTdCssStyle
0616: .renderInline());
0617: stateTdElement.appendChild(stateIconElement);
0618: }
0619:
0620: /**
0621: * Renders the content of the <code>TriCellTable</code> cell which
0622: * contains the button's text.
0623: * Text is always rendered in cell #0 of the table.
0624: *
0625: * @param tct the <code>TriCellTable</code> to update
0626: * @param textNode the text
0627: * @param button the <code>AbstractButton</code> being rendered
0628: */
0629: private void renderCellText(TriCellTable tct, Text textNode,
0630: AbstractButton button) {
0631: Element textTdElement = tct.getTdElement(0);
0632: CssStyle textTdCssStyle = new CssStyle();
0633:
0634: if (Boolean.FALSE.equals(button
0635: .getRenderProperty(AbstractButton.PROPERTY_LINE_WRAP))) {
0636: textTdCssStyle.setAttribute("white-space", "nowrap");
0637: }
0638:
0639: Alignment alignment = combineAlignment(
0640: (Alignment) button
0641: .getRenderProperty(AbstractButton.PROPERTY_TEXT_ALIGNMENT),
0642: (Alignment) button
0643: .getRenderProperty(AbstractButton.PROPERTY_ALIGNMENT));
0644: AlignmentRender
0645: .renderToStyle(textTdCssStyle, alignment, button);
0646:
0647: if (textTdCssStyle.hasAttributes()) {
0648: textTdElement.setAttribute("style", textTdCssStyle
0649: .renderInline());
0650: }
0651:
0652: textTdElement.appendChild(textNode);
0653: }
0654:
0655: /**
0656: * Render default CSS style.
0657: */
0658: private CssStyle renderDefaultStyle(RenderContext rc,
0659: AbstractButton button) {
0660: CssStyle cssStyle = new CssStyle();
0661: LayoutDirectionRender.renderToStyle(cssStyle, button
0662: .getLayoutDirection(), button.getLocale());
0663: ExtentRender.renderToStyle(cssStyle, "width", (Extent) button
0664: .getRenderProperty(AbstractButton.PROPERTY_WIDTH));
0665: Extent height = (Extent) button
0666: .getRenderProperty(AbstractButton.PROPERTY_HEIGHT);
0667: if (height != null) {
0668: ExtentRender.renderToStyle(cssStyle, "height", height);
0669: cssStyle.setAttribute("overflow", "hidden");
0670: }
0671: if (Boolean.FALSE.equals(button
0672: .getRenderProperty(AbstractButton.PROPERTY_LINE_WRAP))) {
0673: cssStyle.setAttribute("white-space", "nowrap");
0674: }
0675:
0676: boolean renderEnabled = button.isRenderEnabled();
0677:
0678: Border border;
0679: Color foreground, background;
0680: Font font;
0681: FillImage backgroundImage;
0682: if (!renderEnabled) {
0683: // Retrieve disabled style information.
0684: background = (Color) button
0685: .getRenderProperty(AbstractButton.PROPERTY_DISABLED_BACKGROUND);
0686: backgroundImage = (FillImage) button
0687: .getRenderProperty(AbstractButton.PROPERTY_DISABLED_BACKGROUND_IMAGE);
0688: border = (Border) button
0689: .getRenderProperty(AbstractButton.PROPERTY_DISABLED_BORDER);
0690: font = (Font) button
0691: .getRenderProperty(AbstractButton.PROPERTY_DISABLED_FONT);
0692: foreground = (Color) button
0693: .getRenderProperty(AbstractButton.PROPERTY_DISABLED_FOREGROUND);
0694:
0695: // Fallback to normal styles.
0696: if (background == null) {
0697: background = (Color) button
0698: .getRenderProperty(AbstractButton.PROPERTY_BACKGROUND);
0699: if (backgroundImage == null) {
0700: // Special case:
0701: // Disabled background without disabled background image will render disabled background instead of
0702: // normal background image.
0703: backgroundImage = (FillImage) button
0704: .getRenderProperty(AbstractButton.PROPERTY_BACKGROUND_IMAGE);
0705: }
0706: }
0707: if (border == null) {
0708: border = (Border) button
0709: .getRenderProperty(AbstractButton.PROPERTY_BORDER);
0710: }
0711: if (font == null) {
0712: font = (Font) button
0713: .getRenderProperty(AbstractButton.PROPERTY_FONT);
0714: }
0715: if (foreground == null) {
0716: foreground = (Color) button
0717: .getRenderProperty(AbstractButton.PROPERTY_FOREGROUND);
0718: }
0719: } else {
0720: border = (Border) button
0721: .getRenderProperty(AbstractButton.PROPERTY_BORDER);
0722: foreground = (Color) button
0723: .getRenderProperty(AbstractButton.PROPERTY_FOREGROUND);
0724: background = (Color) button
0725: .getRenderProperty(AbstractButton.PROPERTY_BACKGROUND);
0726: font = (Font) button
0727: .getRenderProperty(AbstractButton.PROPERTY_FONT);
0728: backgroundImage = (FillImage) button
0729: .getRenderProperty(AbstractButton.PROPERTY_BACKGROUND_IMAGE);
0730: }
0731:
0732: BorderRender.renderToStyle(cssStyle, border);
0733: ColorRender.renderToStyle(cssStyle, foreground, background);
0734: FontRender.renderToStyle(cssStyle, font);
0735: FillImageRender.renderToStyle(cssStyle, rc, this , button,
0736: IMAGE_ID_BACKGROUND, backgroundImage,
0737: FillImageRender.FLAG_DISABLE_FIXED_MODE);
0738: InsetsRender.renderToStyle(cssStyle, "padding", (Insets) button
0739: .getRenderProperty(AbstractButton.PROPERTY_INSETS));
0740:
0741: AlignmentRender.renderToStyle(cssStyle, (Alignment) button
0742: .getRenderProperty(AbstractButton.PROPERTY_ALIGNMENT),
0743: button);
0744: return cssStyle;
0745: }
0746:
0747: /**
0748: * @see nextapp.echo2.webcontainer.ComponentSynchronizePeer#renderDispose(nextapp.echo2.webcontainer.RenderContext,
0749: * nextapp.echo2.app.update.ServerComponentUpdate, nextapp.echo2.app.Component)
0750: */
0751: public void renderDispose(RenderContext rc,
0752: ServerComponentUpdate update, Component component) {
0753: rc.getServerMessage().addLibrary(BUTTON_SERVICE.getId());
0754: renderDisposeDirective(rc, (AbstractButton) component);
0755: }
0756:
0757: /**
0758: * Renders a directive to the outgoing <code>ServerMessage</code> to
0759: * dispose the state of a button, performing tasks such as unregistering
0760: * event listeners on the client.
0761: *
0762: * @param rc the relevant <code>RenderContext</code>
0763: * @param button the button
0764: */
0765: private void renderDisposeDirective(RenderContext rc,
0766: AbstractButton button) {
0767: ServerMessage serverMessage = rc.getServerMessage();
0768: Element itemizedUpdateElement = serverMessage
0769: .getItemizedDirective(ServerMessage.GROUP_ID_PREREMOVE,
0770: "EchoButton.MessageProcessor", "dispose",
0771: new String[0], new String[0]);
0772: Element itemElement = serverMessage.getDocument()
0773: .createElement("item");
0774: itemElement.setAttribute("eid", ContainerInstance
0775: .getElementId(button));
0776: itemizedUpdateElement.appendChild(itemElement);
0777: }
0778:
0779: /**
0780: * @see nextapp.echo2.webcontainer.DomUpdateSupport#renderHtml(nextapp.echo2.webcontainer.RenderContext,
0781: * nextapp.echo2.app.update.ServerComponentUpdate, org.w3c.dom.Node, nextapp.echo2.app.Component)
0782: */
0783: public void renderHtml(RenderContext rc,
0784: ServerComponentUpdate update, Node parentNode,
0785: Component component) {
0786: ServerMessage serverMessage = rc.getServerMessage();
0787: serverMessage.addLibrary(BUTTON_SERVICE.getId());
0788: AbstractButton button = (AbstractButton) component;
0789: Element containerDivElement = renderButtonContainer(rc,
0790: parentNode, button);
0791: renderInitDirective(rc, button);
0792: renderButtonContent(rc, containerDivElement, button);
0793: }
0794:
0795: /**
0796: * Renders a directive to the outgoing <code>ServerMessage</code> to
0797: * initialize the state of a button, performing tasks such as registering
0798: * event listeners on the client.
0799: *
0800: * @param rc the relevant <code>RenderContext</code>
0801: * @param button the button
0802: */
0803: private void renderInitDirective(RenderContext rc,
0804: AbstractButton button) {
0805: String elementId = ContainerInstance.getElementId(button);
0806: ServerMessage serverMessage = rc.getServerMessage();
0807: FillImage backgroundImage = (FillImage) button
0808: .getRenderProperty(AbstractButton.PROPERTY_BACKGROUND_IMAGE);
0809:
0810: boolean rolloverEnabled = ((Boolean) button
0811: .getRenderProperty(
0812: AbstractButton.PROPERTY_ROLLOVER_ENABLED,
0813: Boolean.FALSE)).booleanValue();
0814: boolean pressedEnabled = ((Boolean) button.getRenderProperty(
0815: AbstractButton.PROPERTY_PRESSED_ENABLED, Boolean.FALSE))
0816: .booleanValue();
0817:
0818: String pressedStyle = "";
0819: String rolloverStyle = "";
0820:
0821: String defaultIconUri = null;
0822: String rolloverIconUri = null;
0823: String pressedIconUri = null;
0824:
0825: if (rolloverEnabled || pressedEnabled) {
0826: boolean hasIcon = button
0827: .getRenderProperty(AbstractButton.PROPERTY_ICON) != null;
0828: if (hasIcon) {
0829: defaultIconUri = ImageTools.getUri(rc, this , button,
0830: IMAGE_ID_ICON);
0831: }
0832:
0833: if (rolloverEnabled) {
0834: CssStyle rolloverCssStyle = new CssStyle();
0835: BorderRender
0836: .renderToStyle(
0837: rolloverCssStyle,
0838: (Border) button
0839: .getRenderProperty(AbstractButton.PROPERTY_ROLLOVER_BORDER));
0840: ColorRender
0841: .renderToStyle(
0842: rolloverCssStyle,
0843: (Color) button
0844: .getRenderProperty(AbstractButton.PROPERTY_ROLLOVER_FOREGROUND),
0845: (Color) button
0846: .getRenderProperty(AbstractButton.PROPERTY_ROLLOVER_BACKGROUND));
0847: FontRender
0848: .renderToStyle(
0849: rolloverCssStyle,
0850: (Font) button
0851: .getRenderProperty(AbstractButton.PROPERTY_ROLLOVER_FONT));
0852: if (backgroundImage != null) {
0853: FillImageRender
0854: .renderToStyle(
0855: rolloverCssStyle,
0856: rc,
0857: this ,
0858: button,
0859: IMAGE_ID_ROLLOVER_BACKGROUND,
0860: (FillImage) button
0861: .getRenderProperty(AbstractButton.PROPERTY_ROLLOVER_BACKGROUND_IMAGE),
0862: FillImageRender.FLAG_DISABLE_FIXED_MODE);
0863: }
0864: if (rolloverCssStyle.hasAttributes()) {
0865: rolloverStyle = rolloverCssStyle.renderInline();
0866: }
0867: if (hasIcon) {
0868: ImageReference rolloverIcon = (ImageReference) button
0869: .getRenderProperty(AbstractButton.PROPERTY_ROLLOVER_ICON);
0870: if (rolloverIcon != null) {
0871: rolloverIconUri = ImageTools.getUri(rc, this ,
0872: button, IMAGE_ID_ROLLOVER_ICON);
0873: }
0874: }
0875: }
0876:
0877: if (pressedEnabled) {
0878: CssStyle pressedCssStyle = new CssStyle();
0879: BorderRender
0880: .renderToStyle(
0881: pressedCssStyle,
0882: (Border) button
0883: .getRenderProperty(AbstractButton.PROPERTY_PRESSED_BORDER));
0884: ColorRender
0885: .renderToStyle(
0886: pressedCssStyle,
0887: (Color) button
0888: .getRenderProperty(AbstractButton.PROPERTY_PRESSED_FOREGROUND),
0889: (Color) button
0890: .getRenderProperty(AbstractButton.PROPERTY_PRESSED_BACKGROUND));
0891: FontRender
0892: .renderToStyle(
0893: pressedCssStyle,
0894: (Font) button
0895: .getRenderProperty(AbstractButton.PROPERTY_PRESSED_FONT));
0896: if (backgroundImage != null) {
0897: FillImageRender
0898: .renderToStyle(
0899: pressedCssStyle,
0900: rc,
0901: this ,
0902: button,
0903: IMAGE_ID_PRESSED_BACKGROUND,
0904: (FillImage) button
0905: .getRenderProperty(AbstractButton.PROPERTY_PRESSED_BACKGROUND_IMAGE),
0906: FillImageRender.FLAG_DISABLE_FIXED_MODE);
0907: }
0908: if (pressedCssStyle.hasAttributes()) {
0909: pressedStyle = pressedCssStyle.renderInline();
0910: }
0911: if (hasIcon) {
0912: ImageReference pressedIcon = (ImageReference) button
0913: .getRenderProperty(AbstractButton.PROPERTY_PRESSED_ICON);
0914: if (pressedIcon != null) {
0915: pressedIconUri = ImageTools.getUri(rc, this ,
0916: button, IMAGE_ID_PRESSED_ICON);
0917: }
0918: }
0919: }
0920: }
0921:
0922: CssStyle defaultCssStyle = renderDefaultStyle(rc, button);
0923: String defaultStyle = defaultCssStyle.renderInline();
0924:
0925: Element itemizedUpdateElement = serverMessage
0926: .getItemizedDirective(
0927: ServerMessage.GROUP_ID_POSTUPDATE,
0928: "EchoButton.MessageProcessor", "init",
0929: BUTTON_INIT_KEYS, new String[] { defaultStyle,
0930: rolloverStyle, pressedStyle });
0931: Element itemElement = serverMessage.getDocument()
0932: .createElement("item");
0933: itemElement.setAttribute("eid", elementId);
0934: if (defaultIconUri != null) {
0935: itemElement.setAttribute("default-icon", defaultIconUri);
0936: }
0937: if (rolloverIconUri != null) {
0938: itemElement.setAttribute("rollover-icon", rolloverIconUri);
0939: }
0940: if (pressedIconUri != null) {
0941: itemElement.setAttribute("pressed-icon", pressedIconUri);
0942: }
0943: if (!button.hasActionListeners()) {
0944: itemElement.setAttribute("server-notify", "false");
0945: }
0946: if (!button.isRenderEnabled()) {
0947: itemElement.setAttribute("enabled", "false");
0948: }
0949:
0950: if (button instanceof ToggleButton) {
0951: ToggleButton toggleButton = (ToggleButton) button;
0952: itemElement.setAttribute("toggle", "true");
0953: itemElement.setAttribute("selected", toggleButton
0954: .isSelected() ? "true" : "false");
0955: itemElement.setAttribute("state-icon", ImageTools.getUri(
0956: rc, this , toggleButton, IMAGE_ID_STATE_ICON));
0957: itemElement.setAttribute("selected-state-icon", ImageTools
0958: .getUri(rc, this , toggleButton,
0959: IMAGE_ID_SELECTED_STATE_ICON));
0960:
0961: if (rolloverEnabled
0962: && toggleButton
0963: .getRenderProperty(ToggleButton.PROPERTY_ROLLOVER_STATE_ICON) != null
0964: && toggleButton
0965: .getRenderProperty(ToggleButton.PROPERTY_ROLLOVER_SELECTED_STATE_ICON) != null) {
0966: itemElement.setAttribute("rollover-state-icon",
0967: ImageTools.getUri(rc, this , toggleButton,
0968: IMAGE_ID_ROLLOVER_STATE_ICON));
0969: itemElement.setAttribute(
0970: "rollover-selected-state-icon",
0971: ImageTools.getUri(rc, this , toggleButton,
0972: IMAGE_ID_ROLLOVER_SELECTED_STATE_ICON));
0973: }
0974: if (pressedEnabled
0975: && toggleButton
0976: .getRenderProperty(ToggleButton.PROPERTY_PRESSED_STATE_ICON) != null
0977: && toggleButton
0978: .getRenderProperty(ToggleButton.PROPERTY_PRESSED_SELECTED_STATE_ICON) != null) {
0979: itemElement.setAttribute("pressed-state-icon",
0980: ImageTools.getUri(rc, this , toggleButton,
0981: IMAGE_ID_PRESSED_STATE_ICON));
0982: itemElement.setAttribute("pressed-selected-state-icon",
0983: ImageTools.getUri(rc, this , toggleButton,
0984: IMAGE_ID_PRESSED_SELECTED_STATE_ICON));
0985: }
0986:
0987: if (button instanceof RadioButton) {
0988: ButtonGroup buttonGroup = ((RadioButton) toggleButton)
0989: .getGroup();
0990: if (buttonGroup != null) {
0991: rc.getContainerInstance().getIdTable().register(
0992: buttonGroup);
0993: itemElement.setAttribute("group", buttonGroup
0994: .getRenderId());
0995: }
0996: }
0997: }
0998:
0999: itemizedUpdateElement.appendChild(itemElement);
1000: }
1001:
1002: /**
1003: * @see nextapp.echo2.webcontainer.ComponentSynchronizePeer#renderUpdate(nextapp.echo2.webcontainer.RenderContext,
1004: * nextapp.echo2.app.update.ServerComponentUpdate, java.lang.String)
1005: */
1006: public boolean renderUpdate(RenderContext rc,
1007: ServerComponentUpdate update, String targetId) {
1008: String parentId = ContainerInstance.getElementId(update
1009: .getParent());
1010: DomUpdate.renderElementRemove(rc.getServerMessage(), parentId);
1011: renderAdd(rc, update, targetId, update.getParent());
1012: return false;
1013: }
1014: }
|