0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041: package org.netbeans.modules.visualweb.designer;
0042:
0043: import org.netbeans.modules.visualweb.api.designer.DomProvider;
0044: import org.netbeans.modules.visualweb.api.designer.DomProvider.DomPosition;
0045: import org.netbeans.modules.visualweb.api.designer.markup.MarkupService;
0046: import org.netbeans.modules.visualweb.text.DesignerPaneBase;
0047: import java.awt.Component;
0048: import java.awt.datatransfer.Transferable;
0049: import java.awt.event.ActionEvent;
0050: import java.awt.event.FocusEvent;
0051: import java.awt.event.FocusListener;
0052: import javax.swing.Action;
0053: import javax.swing.JRootPane;
0054: import org.apache.xerces.dom.events.MutationEventImpl;
0055: import org.netbeans.modules.visualweb.css2.CssBox;
0056: import org.netbeans.modules.visualweb.css2.SpaceBox;
0057: import org.netbeans.modules.visualweb.css2.TextBox;
0058:
0059: import org.w3c.dom.DocumentFragment;
0060: import org.w3c.dom.Element;
0061: import org.w3c.dom.Node;
0062: import org.w3c.dom.NodeList;
0063: import org.w3c.dom.events.EventListener;
0064: import org.w3c.dom.events.EventTarget;
0065:
0066: /**
0067: * Editor for inline-editing text of components where the "model" is
0068: * just an attribute on a component. For example, the JSF "HtmlOutputText"
0069: * component has the text stored in the "value" attribute; when you inline
0070: * edit the rendered text from the output text you are really editing
0071: * a DOM element attribute.
0072: *
0073: * For now this is only for JSF components.
0074: *
0075: * @todo Reinstate the <br> creation code in start()
0076: *
0077: * @author Tor Norbye
0078: */
0079: class AttributeInlineEditor extends InlineEditor /*implements org.w3c.dom.events.EventListener*/{
0080: // XXX Moved to designer/jsf/../InlineEditorSupportImpl.
0081: // private boolean hasBeenEdited = false;
0082: // private DesignProperty property;
0083: // private DocumentFragment fragment;
0084: // XXX Moved to designer/jsf/../InlineEditorSupportImpl.
0085: // private FacesPageUnit facesPageUnit;
0086: // XXX Moved to designer/jsf/../InlineEditorSupportImpl.
0087: // private Node text;
0088: // private Node br;
0089: // XXX Moved to designer/jsf/../InlineEditorSupportImpl.
0090: // private String xpath;
0091:
0092: private final FocusListener focusListener = new AttributeInlineEditorFocusListener(
0093: this );
0094:
0095: // AttributeInlineEditor(WebForm webform, /*MarkupDesignBean bean, DesignProperty property,*/
0096: // String xpath,DomProvider.InlineEditorSupport inlineEditorSupport) {
0097: AttributeInlineEditor(WebForm webform, /*MarkupDesignBean bean, DesignProperty property,
0098: String xpath,*/
0099: DomProvider.InlineEditorSupport inlineEditorSupport) {
0100: // super(webform, bean, property.getPropertyDescriptor().getName());
0101: // super(webform, /*bean,*/ inlineEditorSupport);
0102: // this.property = property;
0103: // this.xpath = xpath;
0104: super (webform, /*bean,*/inlineEditorSupport);
0105: }
0106:
0107: // public static AttributeInlineEditor get(WebForm webform, String xpath, Element componentRootElement,DomProvider.InlineEditorSupport inlineEditorSupport) {
0108: public static AttributeInlineEditor get(WebForm webform, /*String xpath,*/
0109: Element componentRootElement,
0110: DomProvider.InlineEditorSupport inlineEditorSupport) {
0111: // if (!isEditingAllowed(property)) {
0112: if (!inlineEditorSupport.isEditingAllowed()) {
0113: return null;
0114: }
0115:
0116: // TEMPORARY RESTRICTION, not inherent. Gotta address
0117: // issues like how to identify the text node initially,
0118: // how to create linebreaks in the document fragment
0119: // and have them added to as DesignBeans (br is a DesignBean)
0120: // when committed, etc.
0121: // if (!isEscaped(bean)) {
0122: if (!isEscaped(webform, componentRootElement)) {
0123: // Don't support editing unescaped properties yet...
0124: return null;
0125: }
0126:
0127: // TODO: Ensure that the property is of String type?? I don't
0128: // support anything else... (and how could you inline text edit
0129: // anything else?)
0130: // return new AttributeInlineEditor(webform, /*bean, property,*/ xpath, inlineEditorSupport);
0131: return new AttributeInlineEditor(webform, /*bean, property, xpath,*/
0132: inlineEditorSupport);
0133: }
0134:
0135: // XXX Moved to designer/jsf/../InlineEditorSupportImpl
0136: // /** Return the text node containing the value attribute.
0137: // * This is simplified. Later, what if you have an output text
0138: // * with this value: "<b>Hello World</b>". I won't find that; I need
0139: // * to search for the <b> node etc.
0140: // */
0141: // private Node findTextNode(Node root) {
0142: // assert root != null;
0143: //
0144: //// // String assumption should be checked in beandescriptor search for TEXT_NODE_PROPERTY,
0145: //// // especially if we publish this property. Or we could at least specify that the
0146: //// // property MUST be a String.
0147: //// String value = (String)property.getValue();
0148: // String value = inlineEditorSupport.getValue();
0149: //
0150: // // Search for the special node which contains the text.
0151: // // Special case empty... look for span with a CSS class of
0152: // // "rave-uninitialized-text".
0153: // if ((value == null) || (value.length() == 0)) {
0154: // // User has not set text; renderer may have done something
0155: // // like insert "Text" with a special CSS style so look for
0156: // // the element with that style and take its children to
0157: // // be the content to be edited.
0158: // // First see if we can find an eligible text node
0159: // Node text = findFirstTextNode(root);
0160: //
0161: // if (text == null) {
0162: // text = findDesignStyleClass(root);
0163: // }
0164: //
0165: // return text;
0166: // } else {
0167: // return findText(root, value);
0168: // }
0169: // }
0170: //
0171: // private Node findFirstTextNode(Node node) {
0172: // if (node.getNodeType() == Node.TEXT_NODE) {
0173: // String s = node.getNodeValue();
0174: //
0175: // if (!DesignerUtils.onlyWhitespace(s)) {
0176: // return node;
0177: // }
0178: // }
0179: //
0180: // NodeList nl = node.getChildNodes();
0181: //
0182: // for (int i = 0, n = nl.getLength(); i < n; i++) {
0183: // Node child = nl.item(i);
0184: //
0185: // // Don't pick text children in JavaScript or CSS subtrees
0186: // if ((child.getNodeType() == Node.ELEMENT_NODE) &&
0187: // (node.getNodeName().equals(HtmlTag.SCRIPT.name) ||
0188: // node.getNodeName().equals(HtmlTag.STYLE.name))) {
0189: // continue;
0190: // }
0191: //
0192: // Node match = findFirstTextNode(child);
0193: //
0194: // if (match != null) {
0195: // return match;
0196: // }
0197: // }
0198: //
0199: // return null;
0200: // }
0201: //
0202: // private Node findDesignStyleClass(Node node) {
0203: // if (node.getNodeType() == Node.ELEMENT_NODE) {
0204: // Element element = (Element)node;
0205: //
0206: // if (element.getAttribute(HtmlAttribute.CLASS).indexOf("rave-uninitialized-text") != -1) { // NOI18N
0207: //
0208: // return node;
0209: // }
0210: // }
0211: //
0212: // NodeList nl = node.getChildNodes();
0213: //
0214: // for (int i = 0, n = nl.getLength(); i < n; i++) {
0215: // Node match = findDesignStyleClass(nl.item(i));
0216: //
0217: // if (match != null) {
0218: // return match;
0219: // }
0220: // }
0221: //
0222: // return null;
0223: // }
0224: //
0225: // private Node findText(Node node, String value) {
0226: // //XXX rewrite this!!!
0227: // if (node.getNodeType() == Node.TEXT_NODE) {
0228: // String nodeText = node.getNodeValue();
0229: // int index = nodeText.indexOf(value);
0230: //
0231: // if ((index != -1) && (value.length() > 0)) {
0232: // // Possibly split the text node node, in case it contains
0233: // // "additional junk". For example, the table header will
0234: // // render a row count into this text node too, which is not
0235: // // part of the property value, so we don't want it included
0236: // // in our text editing efforts.
0237: // org.w3c.dom.Text text = (org.w3c.dom.Text)node;
0238: //
0239: // if (text.getLength() != value.length()) {
0240: // // Strip text before the value text
0241: // if (index != 0) {
0242: // node = text.splitText(index);
0243: // text = (org.w3c.dom.Text)node;
0244: // }
0245: //
0246: // // Strip off text after the value text
0247: // if (text.getLength() > value.length()) {
0248: // text.splitText(value.length());
0249: // }
0250: // }
0251: //
0252: // return node;
0253: // }
0254: // }
0255: //
0256: // NodeList nl = node.getChildNodes();
0257: //
0258: // for (int i = 0, n = nl.getLength(); i < n; i++) {
0259: // Node match = findText(nl.item(i), value);
0260: //
0261: // if (match != null) {
0262: // return match;
0263: // }
0264: // }
0265: //
0266: // return null;
0267: // }
0268:
0269: public void start(boolean selectText, String initialEdit) {
0270: //// facesPageUnit = FacesSupport.getFacesUnit(webform.getModel().getLiveUnit());
0271: //// facesPageUnit = getFacesUnit(webform.getModel().getLiveUnit());
0272: ////
0273: //// if (facesPageUnit == null) {
0274: //// return;
0275: //// }
0276: //
0277: //// fragment = webform.getDomSynchronizer().createSourceFragment(bean);
0278: //// fragment = webform.createSourceFragment(bean);
0279: // fragment = inlineEditorSupport.createSourceFragment();
0280: //
0281: // if (fragment == null) {
0282: // return;
0283: // }
0284: //
0285: //// facesPageUnit.setPreRendered(bean, fragment);
0286: //// if (!webform.setPrerenderedBean(bean, fragment)) {
0287: // if (!inlineEditorSupport.setPrerendered(fragment)) {
0288: // return;
0289: // }
0290: //
0291: // // Select the text in the component and set the
0292: // // caret to the end
0293: // DesignerPane pane = webform.getPane();
0294: //
0295: //// if (pane.getCaret() == null) {
0296: //// DesignerCaret dc = pane.getPaneUI().createCaret();
0297: //// webform.getPane().setCaret(dc);
0298: //// }
0299: // if (!pane.hasCaret()) {
0300: // pane.createCaret();
0301: // }
0302: //
0303: // Node n = null;
0304: //
0305: // if (xpath != null) {
0306: //// n = findPropertyNode(fragment, xpath);
0307: // n = WebForm.getDomProviderService().findPropertyNode(fragment, xpath);
0308: //
0309: //// if ((n == null) && (property.getValueSource() == null)) {
0310: // if ((n == null) && (inlineEditorSupport.getValueSource() == null)) {
0311: // // It's possible that we're editing a property that hasn't
0312: // // resulted in any markup in the component yet if it's not
0313: // // set. For example, an attempt to edit the "label" property
0314: // // of a text field. This should edit xpath "//span", but there's
0315: // // no <span> rendered until label is set to something. So,
0316: // // if this is the case, set the property temporarily, render
0317: // // the fragment, and unset it.
0318: //// String oldPropertyValue = (String)property.getValue();
0319: // String oldPropertyValue = inlineEditorSupport.getValue();
0320: //
0321: //// final String MARKER = property.getPropertyDescriptor().getDisplayName();
0322: // final String MARKER = inlineEditorSupport.getDisplayName();
0323: //
0324: //// try {
0325: //// Method m = property.getPropertyDescriptor().getWriteMethod();
0326: //// m.invoke(bean.getInstance(), new Object[] { MARKER });
0327: // inlineEditorSupport.setViaWriteMethod(MARKER);
0328: //// } catch (Exception ex) {
0329: //// ErrorManager.getDefault().notify(ex);
0330: //// }
0331: //
0332: // try {
0333: //// facesPageUnit.setPreRendered(null, null);
0334: //// webform.setPrerenderedBean(null, null);
0335: // inlineEditorSupport.clearPrerendered();
0336: //
0337: // // XXX TODO There is not needed webform here.
0338: //// FileObject markupFile = webform.getModel().getMarkupFile();
0339: //// fragment = FacesSupport.renderHtml(markupFile, bean, false);
0340: //// fragment = InSyncService.getProvider().renderHtml(markupFile, bean);
0341: //// fragment = webform.renderHtmlForMarkupDesignBean(bean);
0342: // fragment = inlineEditorSupport.renderDomFragment();
0343: // // XXX Moved into the impl (of the above method).
0344: //// // XXX To get it into source document so it can work (Positions work only against source doc!).
0345: //// fragment = (DocumentFragment)webform.getJspDom().importNode(fragment, true);
0346: //
0347: //// // XXX FIXME Is this correct here?
0348: // // XXX Moving into designer/jsf/../InlineEditorSupport
0349: //// webform.updateErrorsInComponent();
0350: //
0351: //// facesPageUnit.setPreRendered(bean, fragment);
0352: //// webform.setPrerenderedBean(bean, fragment);
0353: // inlineEditorSupport.setPrerendered(fragment);
0354: //
0355: //// n = findPropertyNode(fragment, xpath);
0356: // n = WebForm.getDomProviderService().findPropertyNode(fragment, xpath);
0357: // selectText = true;
0358: //// webform.getDomSynchronizer().requestChange(bean);
0359: //// webform.requestChange(bean);
0360: // inlineEditorSupport.requestChange();
0361: // } finally {
0362: //// try {
0363: //// Method m = property.getPropertyDescriptor().getWriteMethod();
0364: //// m.invoke(bean.getInstance(), new Object[] { oldPropertyValue });
0365: // inlineEditorSupport.setViaWriteMethod(oldPropertyValue);
0366: //// } catch (Exception ex) {
0367: //// ErrorManager.getDefault().notify(ex);
0368: //// }
0369: // }
0370: // }
0371: // }
0372: //
0373: // if (n != null) {
0374: // // See if I can find a text node inside it
0375: // text = findTextNode(n);
0376: //
0377: // if (text == null) {
0378: // text = n;
0379: // }
0380: // } else {
0381: // text = findTextNode(fragment);
0382: // }
0383: //
0384: // if (text != null) {
0385: // // Put a line break at the end of the text to ensure that we have a possible caret position
0386: // // if the user erases all text
0387: // //// XXX unfortunately this leads to comparisons failing in LineBoxGroup.paintBackground
0388: // // so the selection winds up not getting painted. I've gotta investigate this.
0389: // //if (isEscaped()) {
0390: // // br = text.getParentNode().appendChild(text.getOwnerDocument().createElement("br"));
0391: // //}
0392: // if (text.getNodeType() == Node.TEXT_NODE) {
0393: //// begin = new Position(text, 0, Bias.FORWARD);
0394: //// begin = Position.create(text, 0, Bias.FORWARD);
0395: // begin = webform.createDomPosition(text, 0, Bias.FORWARD);
0396: //
0397: //// if (br != null) {
0398: //// end = Position.create(br, false);
0399: //// } else {
0400: //// end = new Position(text, text.getNodeValue().length(), Bias.BACKWARD);
0401: //// end = Position.create(text, text.getNodeValue().length(), Bias.BACKWARD);
0402: // end = webform.createDomPosition(text, text.getNodeValue().length(), Bias.BACKWARD);
0403: //// }
0404: // } else {
0405: // NodeList children = text.getChildNodes();
0406: //
0407: // if (children.getLength() > 0) {
0408: //// begin = new Position(children.item(0), 0, Bias.FORWARD);
0409: //// begin = Position.create(children.item(0), 0, Bias.FORWARD);
0410: // begin = webform.createDomPosition(children.item(0), 0, Bias.FORWARD);
0411: //
0412: //// if (br != null) {
0413: //// end = Position.create(br, false);
0414: //// } else {
0415: // Node last = children.item(children.getLength() - 1);
0416: //
0417: // if (last.getNodeType() == Node.TEXT_NODE) {
0418: //// end = new Position(last, last.getNodeValue().length(), Bias.BACKWARD);
0419: //// end = Position.create(last, last.getNodeValue().length(), Bias.BACKWARD);
0420: // end = webform.createDomPosition(last, last.getNodeValue().length(), Bias.BACKWARD);
0421: // } else {
0422: //// end = new Position(last, last.getChildNodes().getLength(), Bias.BACKWARD);
0423: //// end = Position.create(last, last.getChildNodes().getLength(), Bias.BACKWARD);
0424: // end = webform.createDomPosition(last, last.getChildNodes().getLength(), Bias.BACKWARD);
0425: // }
0426: //// }
0427: // } else {
0428: //// begin = new Position(text, 0, Bias.FORWARD);
0429: //// begin = Position.create(text, 0, Bias.FORWARD);
0430: // begin = webform.createDomPosition(text, 0, Bias.FORWARD);
0431: //
0432: //// if (br != null) {
0433: //// end = Position.create(br, false);
0434: //// } else {
0435: //// end = new Position(text, text.getChildNodes().getLength(), Bias.BACKWARD);
0436: //// end = Position.create(text, text.getChildNodes().getLength(), Bias.BACKWARD);
0437: // end = webform.createDomPosition(text, text.getChildNodes().getLength(), Bias.BACKWARD);
0438: //// }
0439: // }
0440: // }
0441: // } else {
0442: //// begin = new Position(fragment, 0, Bias.FORWARD);
0443: //// end = new Position(fragment, fragment.getChildNodes().getLength(), Bias.FORWARD);
0444: //// begin = Position.create(fragment, 0, Bias.FORWARD);
0445: //// end = Position.create(fragment, fragment.getChildNodes().getLength(), Bias.FORWARD);
0446: // begin = webform.createDomPosition(fragment, 0, Bias.FORWARD);
0447: // end = webform.createDomPosition(fragment, fragment.getChildNodes().getLength(), Bias.FORWARD);
0448: // }
0449: selectText = inlineEditorSupport
0450: .prepareAttributeInlineEditor(selectText);
0451:
0452: // Select the text in the component and set the
0453: // caret to the end
0454: DesignerPane pane = webform.getPane();
0455: // if (pane.getCaret() == null) {
0456: // DesignerCaret dc = pane.getPaneUI().createCaret();
0457: // webform.getPane().setCaret(dc);
0458: // }
0459: if (!pane.hasCaret()) {
0460: pane.createCaret();
0461: }
0462:
0463: // XXX #94269 It seems it is working better withou these style juggling.
0464: // boolean changed = DesignerUtils.stripDesignStyleClasses(fragment);
0465: //
0466: //// NodeList children = fragment.getChildNodes();
0467: ////
0468: //// for (int i = 0; i < children.getLength(); i++) {
0469: //// Node child = children.item(i);
0470: ////
0471: //// if (child.getNodeType() == Node.ELEMENT_NODE) {
0472: ////// RaveElement e = (RaveElement)child;
0473: //// Element e = (Element)child;
0474: ////// CssLookup.getCssEngine(e).clearComputedStyles(e, null);
0475: ////// CssProvider.getEngineService().clearComputedStylesForElement(e);
0476: //// Element beanElement = bean.getElement();
0477: //// // XXX #6489063 Inherit the style from the original element.
0478: //// // Maybe there should be just the size of the font inherited.
0479: //// CssProvider.getEngineService().setStyleParentForElement(e, beanElement);
0480: ////
0481: ////// e = e.getRendered();
0482: //// e = MarkupService.getRenderedElementForElement(e);
0483: //// Element beanRenderedElement = MarkupService.getRenderedElementForElement(beanElement);
0484: //// if (e != null && beanRenderedElement != null) {
0485: ////// CssLookup.getCssEngine(e).clearComputedStyles(e, null);
0486: ////// CssProvider.getEngineService().clearComputedStylesForElement(e);
0487: //// // XXX #6489063 Inherit the style from the original element.
0488: //// // Maybe there should be just the size of the font inherited.
0489: //// CssProvider.getEngineService().setStyleParentForElement(e, beanRenderedElement);
0490: //// }
0491: //// }
0492: //// }
0493: // inlineEditorSupport.setStyleParent(fragment);
0494: //
0495: // registerDomListeners();
0496:
0497: boolean handled = false;
0498:
0499: // if (!NO_EDIT_VB_EXPR) {
0500: // String value = property.getValueSource();
0501: //
0502: //// if ((value != null) && FacesSupport.isValueBindingExpression(value, false)) {
0503: // if ((value != null) && WebForm.getDomProviderService().isValueBindingExpression(value, false)) {
0504: // // Show vb text in rendered view
0505: // pane.select(begin, end);
0506: // pane.getCaret().replaceSelection(value);
0507: // handled = true;
0508: // }
0509: // }
0510: DomPosition begin = inlineEditorSupport.getBeginPosition();
0511: DomPosition end = inlineEditorSupport.getEndPosition();
0512:
0513: String specialValue = inlineEditorSupport.getSpecialInitValue();
0514: if (specialValue != null) {
0515: // Show vb text in rendered view
0516: pane.select(begin, end);
0517: // pane.getCaret().replaceSelection(specialValue);
0518: pane.replaceSelection(specialValue);
0519:
0520: handled = true;
0521: }
0522:
0523: if (handled) {
0524: // Done
0525: } else if ((initialEdit != null) && (initialEdit.length() > 0)) {
0526: // String value = property.getValueSource();
0527: String value = inlineEditorSupport.getValueSource();
0528:
0529: if (value == null) {
0530: // Shadow text -- replace it completely
0531: pane.select(begin, end);
0532: // pane.getCaret().replaceSelection(initialEdit);
0533: pane.replaceSelection(initialEdit);
0534: } else {
0535: // User has already entered a value - simply append
0536: // pane.setCaretPosition(end);
0537: pane.setCaretDot(end);
0538: // pane.getCaret().replaceSelection(initialEdit);
0539: pane.replaceSelection(initialEdit);
0540: }
0541: } else if (selectText) {
0542: pane.select(begin, end);
0543: } else {
0544: pane.showCaret(end);
0545: }
0546:
0547: // if (changed) {
0548: //// webform.getDomSynchronizer().requestChange(bean);
0549: //// webform.requestChange(bean);
0550: // inlineEditorSupport.requestChange();
0551: // }
0552:
0553: // TODO Set up key bindings such that Ctrl-B will toggle bold,
0554: // Ctrl-I will toggle italics, Enter will insert a newline <br>,
0555: // etc
0556:
0557: // XXX #6431953.
0558: webform.getPane().addFocusListener(focusListener);
0559: }
0560:
0561: // // XXX Moved from FacesSupport.
0562: // /** Find the faces unit associated with the given context (if any) */
0563: // private static FacesPageUnit getFacesUnit(DesignContext context) {
0564: // LiveUnit lu = (LiveUnit)context;
0565: // BeansUnit bu = lu.getBeansUnit();
0566: //
0567: // if (bu instanceof FacesPageUnit) {
0568: // FacesPageUnit fu = (FacesPageUnit)bu;
0569: //
0570: // return fu;
0571: // }
0572: //
0573: // return null;
0574: // }
0575:
0576: public void finish(boolean cancel) {
0577: // XXX #6431953.
0578: webform.getPane().removeFocusListener(focusListener);
0579:
0580: // XXX This logic should be moved somewhere else.
0581: // XXX in flow mode, gotta set it to some other valid position
0582: webform.getPane().setCaret(null);
0583:
0584: //// if (facesPageUnit != null) {
0585: //// facesPageUnit.setPreRendered(null, null);
0586: //// }
0587: //// webform.setPrerenderedBean(null, null);
0588: // inlineEditorSupport.clearPrerendered();
0589: //
0590: // DocumentFragment fragment = inlineEditorSupport.getFragment();
0591: // if (fragment != null) {
0592: // unregisterDomListeners();
0593: // }
0594: //
0595: //// // XXX #6431953.
0596: //// webform.getPane().removeFocusListener(focusListener);
0597: ////
0598: //// // XXX This logic should be moved somewhere else.
0599: //// // XXX in flow mode, gotta set it to some other valid position
0600: //// webform.getPane().setCaret(null);
0601: //
0602: // if (cancel) {
0603: // // Update component rendering the rendered html may contain the
0604: // // rendered value
0605: //// webform.getDomSynchronizer().beanChanged(bean);
0606: //// webform.beanChanged(bean);
0607: // inlineEditorSupport.beanChanged();
0608: //
0609: // return; // don't apply value
0610: // }
0611: //
0612: // // Don't apply changes if we haven't made any edits; that way,
0613: // // we won't change a null value (rendered into "Text") into "Text"
0614: // // which really has a "null" value
0615: // if (!hasBeenEdited) {
0616: // // Ensure that we re-render the component anyway
0617: // // such that the rendered-references are correct again
0618: //// webform.getDomSynchronizer().requestChange(bean);
0619: //// webform.requestChange(bean);
0620: // inlineEditorSupport.requestChange();
0621: //
0622: // return;
0623: // }
0624: //
0625: // // Determine what part of the fragment should be part of the
0626: // // value text!
0627: // // XXX for now use the whole enchilada!!
0628: // String value = null;
0629: // Node text = inlineEditorSupport.getText();
0630: // Node origin = text;
0631: //
0632: // if (origin == null) {
0633: // origin = fragment;
0634: //
0635: // if (origin == null) {
0636: // return;
0637: // }
0638: // } else {
0639: // if (origin.getNodeType() == Node.TEXT_NODE) {
0640: // //origin = origin.getParentNode();
0641: // value = text.getNodeValue();
0642: // }
0643: // }
0644: //
0645: // if (value == null) {
0646: // // Get rid of our temporary newline
0647: //// if (br != null) {
0648: //// br.getParentNode().removeChild(br);
0649: //// }
0650: //
0651: // // Commit value
0652: // StringBuffer sb = new StringBuffer(300);
0653: // NodeList children = origin.getChildNodes();
0654: //
0655: // for (int i = 0, n = children.getLength(); i < n; i++) {
0656: // Node child = children.item(i);
0657: //// sb.append(InSyncService.getProvider().getHtmlStream(child));
0658: // sb.append(WebForm.getDomProviderService().getHtmlStream(child));
0659: // }
0660: //
0661: // value = sb.toString();
0662: // }
0663: //
0664: // if (isEscaped()) {
0665: //// value =
0666: //// // <markup_separation>
0667: ////// MarkupServiceProvider.getDefault().expandHtmlEntities(value, false,
0668: ////// bean.getElement());
0669: //// // ====
0670: ////// InSyncService.getProvider().expandHtmlEntities(value, false, bean.getElement());
0671: //// WebForm.getDomProviderService().expandHtmlEntities(value, false, bean.getElement());
0672: //// // </markup_separation>\
0673: // value = inlineEditorSupport.expandHtmlEntities(value, false);
0674: // }
0675: //
0676: // if ((value != null) && (value.length() == 0)) {
0677: //// property.unset();
0678: // inlineEditorSupport.unset();
0679: // } else {
0680: //// property.setValue(value);
0681: // inlineEditorSupport.setValue(value);
0682: // }
0683: inlineEditorSupport.cleanAttributeInlineEditor(cancel);
0684: }
0685:
0686: public boolean isDocumentEditor() {
0687: return true;
0688: }
0689:
0690: public boolean isEscaped() {
0691: // return isEscaped(bean);
0692: return inlineEditorSupport.isEscaped();
0693: }
0694:
0695: /** Determine if the given bean is escaped */
0696: private/*public*/static boolean isEscaped(WebForm webForm,
0697: Element componentRootElement) {
0698: // // See if the bean looks like an output text that has escape
0699: // // turned off. If so, it's multiline. All others are considered
0700: // // single line.
0701: // if (bean.getInstance() instanceof javax.faces.component.html.HtmlOutputText) {
0702: // DesignProperty escape = bean.getProperty("escape"); // NOI18N
0703: //
0704: // if (escape != null) {
0705: // Object o = escape.getValue();
0706: //
0707: // if (o instanceof Boolean) {
0708: // return ((Boolean)o).booleanValue();
0709: // }
0710: // }
0711: // }
0712: //
0713: // return true;
0714: return webForm.getDomProviderService().isEscapedComponent(
0715: componentRootElement);
0716: }
0717:
0718: public boolean isMultiLine() {
0719: // See if the bean looks like an output text that has escape
0720: // turned off. If so, it's multiline. All others are considered
0721: // single line.
0722: return !isEscaped();
0723: }
0724:
0725: // XXX Moved to designer/jsf/../InlineEditorSupportImpl.
0726: // private void registerDomListeners() {
0727: // DocumentFragment fragment = inlineEditorSupport.getFragment();
0728: // if (fragment == null) {
0729: // return;
0730: // }
0731: //
0732: // EventTarget target = (EventTarget)fragment;
0733: // EventListener adapter = this;
0734: // target.addEventListener(MutationEventImpl.DOM_ATTR_MODIFIED, adapter, false);
0735: //
0736: // /* This event seems to be redundant.
0737: // target.addEventListener(MutationEventImpl.DOM_SUBTREE_MODIFIED, adapter, false);
0738: // */
0739: // target.addEventListener(MutationEventImpl.DOM_NODE_INSERTED, adapter, false);
0740: // target.addEventListener(MutationEventImpl.DOM_NODE_INSERTED_INTO_DOCUMENT, adapter, false);
0741: // target.addEventListener(MutationEventImpl.DOM_NODE_REMOVED, adapter, false);
0742: // target.addEventListener(MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT, adapter, false);
0743: // target.addEventListener(MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED, adapter, false);
0744: //
0745: //// target.addEventListener(InSyncService.DOM_DOCUMENT_REPLACED, adapter, false);
0746: // target.addEventListener(WebForm.getDomProviderService().getDomDocumentReplacedEventConstant(), adapter, false);
0747: // }
0748: //
0749: // private void unregisterDomListeners() {
0750: // DocumentFragment fragment = inlineEditorSupport.getFragment();
0751: // if (fragment == null) {
0752: // return;
0753: // }
0754: //
0755: // EventTarget target = (EventTarget)fragment;
0756: // EventListener adapter = this;
0757: // target.removeEventListener(MutationEventImpl.DOM_ATTR_MODIFIED, adapter, false);
0758: //
0759: // /* This event seems to be redundant.
0760: // target.removeEventListener(MutationEventImpl.DOM_SUBTREE_MODIFIED, adapter, false);
0761: // */
0762: // target.removeEventListener(MutationEventImpl.DOM_NODE_INSERTED, adapter, false);
0763: // target.removeEventListener(MutationEventImpl.DOM_NODE_INSERTED_INTO_DOCUMENT, adapter, false);
0764: // target.removeEventListener(MutationEventImpl.DOM_NODE_REMOVED, adapter, false);
0765: // target.removeEventListener(MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT, adapter, false);
0766: // target.removeEventListener(MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED, adapter, false);
0767: //
0768: //// target.removeEventListener(InSyncService.DOM_DOCUMENT_REPLACED, adapter, false);
0769: // target.removeEventListener(WebForm.getDomProviderService().getDomDocumentReplacedEventConstant(), adapter, false);
0770: // }
0771:
0772: // public void handleEvent(final org.w3c.dom.events.Event e) {
0773: // hasBeenEdited = true;
0774: //
0775: // if (e instanceof org.w3c.dom.events.MutationEvent) {
0776: // org.w3c.dom.events.MutationEvent me = (org.w3c.dom.events.MutationEvent)e;
0777: // String old = me.getPrevValue();
0778: // String nw = me.getNewValue();
0779: //
0780: // if (((old != null) && (nw != null) && (old.equals(nw)))) {
0781: // return;
0782: // }
0783: // }
0784: //
0785: // Node n = (Node)e.getTarget();
0786: //
0787: // DomPosition end = inlineEditorSupport.getEndPosition();
0788: // if (n == end.getNode()) {
0789: // // XXX hack Don't do this, I should have Positions be
0790: // // immutable.
0791: //// end.setOffset(n.getNodeValue().length());
0792: //// end = new Position(end.getNode(), n.getNodeValue().length(), end.getBias());
0793: //// end = Position.create(end.getNode(), n.getNodeValue().length(), end.getBias());
0794: //// end = webform.createDomPosition(end.getNode(), n.getNodeValue().length(), end.getBias());
0795: // inlineEditorSupport.setEndPosition(webform.createDomPosition(end.getNode(), n.getNodeValue().length(), end.getBias()));
0796: // }
0797: //
0798: // // /*
0799: // // Node node = (org.w3c.dom.Node)e.getTarget();
0800: // // String type = e.getType();
0801: // // Node parent = node.getParentNode(); // XXX or use getRelatedNode?
0802: // //
0803: // // */
0804: // // dispatchEvent(bean);
0805: // Node node = (org.w3c.dom.Node)e.getTarget();
0806: // Node parent = node.getParentNode(); // XXX or use getRelatedNode?
0807: //
0808: // // Text node or entity node changes should get translated
0809: // // into a change event on their surrounding element...
0810: // // XXX I could possibly handle to rebreak only
0811: // // the LineBreakGroup.... That would save work -ESPECIALLY-
0812: // // for text right within the <body> tag... but optimize that
0813: // // later
0814: // if (!(node instanceof Element) || ((Element)node).getTagName().equals(HtmlTag.BR.name)) { // text, cdata, entity, ...
0815: // node = parent;
0816: // parent = parent.getParentNode();
0817: //
0818: // if (node instanceof Element) {
0819: //// MarkupDesignBean b = ((RaveElement)node).getDesignBean();
0820: //// MarkupDesignBean b = InSyncService.getProvider().getMarkupDesignBeanForElement((Element)node);
0821: // MarkupDesignBean b = WebForm.getDomProviderService().getMarkupDesignBeanForElement((Element)node);
0822: //
0823: // if (b == null) {
0824: // b = bean;
0825: // }
0826: //
0827: //// webform.getDomSynchronizer().requestTextUpdate(b);
0828: // webform.requestTextUpdate(b);
0829: // }
0830: // } else {
0831: //// webform.getDomSynchronizer().requestChange(bean);
0832: // webform.requestChange(bean);
0833: // }
0834: // inlineEditorSupport.handleEvent(e);
0835: // }
0836:
0837: // /** Good for changes only
0838: // ** @todo Rename dispatchBeanChangeEvent */
0839: // private void dispatchEvent(DesignBean bean) {
0840: //
0841: // // Gotta walk up to the most distant ancestor below the form tag
0842: // // E.g. if you change a command button that's in a data table,
0843: // // we need to re-render the entire data table, not just the button,
0844: // // since the button is replicated in every cell!
0845: // FacesPageUnit facesUnit = webform.getModel().getFacesUnit();
0846: // MarkupBean formBean = facesUnit.getDefaultParent();
0847: //
0848: // DesignBean originalBean = bean;
0849: // DesignBean parent = bean.getBeanParent();
0850: // while (parent != null && FacesSupport.getFacesBean(parent) != formBean) {
0851: // bean = parent;
0852: // parent = parent.getBeanParent();
0853: // }
0854: // if (bean == webform.getModel().getRootBean()) {
0855: // // Looks like the bean was not below the form!
0856: // bean = originalBean;
0857: // }
0858: //
0859: // //final Node element = text;
0860: // final Element element = FacesSupport.getElement(bean);
0861: // if (element == null) {
0862: // Thread.dumpStack();
0863: // return;
0864: // }
0865: // SwingUtilities.invokeLater(new Runnable() {
0866: // public void run() {
0867: // org.w3c.dom.Node parent = element.getParentNode();
0868: // //fireChangedUpdate(fElement, EventType.CHANGE, parent, false, null);
0869: // //docListener.changedUpdate(fElement, Document.EventType.CHANGE, parent, false, null);
0870: // PageBox pageBox = webform.getSelection().getPageBox();
0871: // if (pageBox != null) {
0872: // pageBox.changed(element, parent, false);
0873: // }
0874: // }
0875: // }
0876: // );
0877: // }
0878: // public boolean checkPosition(Position pos) {
0879: public boolean checkPosition(DomPosition pos) {
0880: // The TextInlineEditor ought to do this instead:
0881: // return !pos.isRendered()
0882: // Positions can be in the document fragment! Actually, I
0883: // ought to check that it is
0884: Node node = pos.getNode();
0885:
0886: if (node == null) {
0887: return false;
0888: }
0889:
0890: // if (pos.isRendered()) {
0891: // if (MarkupService.isRenderedNode(pos.getNode())) {
0892: if (webform.isRenderedNode(pos.getNode())) {
0893: pos = pos.getSourcePosition();
0894: node = pos.getNode();
0895: }
0896:
0897: if (node == null) {
0898: return false;
0899: }
0900:
0901: Node parent = node.getParentNode();
0902:
0903: DocumentFragment fragment = inlineEditorSupport.getFragment();
0904: while (parent != null) {
0905: if (parent == fragment) {
0906: return true;
0907: }
0908:
0909: parent = parent.getParentNode();
0910: }
0911:
0912: return false;
0913: }
0914:
0915: public Transferable copyText(boolean cut) {
0916: // DesignerCaret caret = webform.getPane().getCaret();
0917: // if (caret != null) {
0918: // return caret.copySelection(cut);
0919: // }
0920: DesignerPane pane = webform.getPane();
0921: if (pane.hasCaret()) {
0922: return pane.copySelection(cut);
0923: }
0924:
0925: return null;
0926: }
0927:
0928: public void invokeDeleteNextCharAction(ActionEvent evt) {
0929: DesignerPane designerPane = webform.getPane();
0930: Action[] actions = designerPane.getActions();
0931: for (Action action : actions) {
0932: if (action != null
0933: && action.getValue(Action.NAME) == DesignerPaneBase.deleteNextCharAction) {
0934: // XXX Pretend the event originated from the DesignerPane, otherwise the action wouldn't work.
0935: ActionEvent fakeEvt = new ActionEvent(designerPane, evt
0936: .getID(), evt.getActionCommand(),
0937: evt.getWhen(), evt.getModifiers());
0938: action.actionPerformed(fakeEvt);
0939: break;
0940: }
0941: }
0942: }
0943:
0944: /** XXX #6431953 When losing focus, the inline editing is finishing.
0945: * XXX All this just tries to impl the strange Leopard behaviour, when popup is invoked
0946: * the inline editing is not finished, but only when some action from the popup is invoked it is finished.
0947: * It needs to be revisited by HIE. */
0948: private static class AttributeInlineEditorFocusListener implements
0949: FocusListener {
0950:
0951: private final AttributeInlineEditor attributeInlineEditor;
0952:
0953: public AttributeInlineEditorFocusListener(
0954: AttributeInlineEditor attributeInlineEditor) {
0955: this .attributeInlineEditor = attributeInlineEditor;
0956: }
0957:
0958: public void focusGained(FocusEvent e) {
0959: }
0960:
0961: public void focusLost(FocusEvent e) {
0962: if (!e.isTemporary()
0963: || e.getComponent() instanceof JRootPane) {
0964: // attributeInlineEditor.finish(false);
0965: attributeInlineEditor.webform.getManager()
0966: .finishInlineEditing(false);
0967: e.getComponent().removeFocusListener(this );
0968: return;
0969: }
0970:
0971: if (e.isTemporary()) {
0972: Component oppositeComponent = e.getOppositeComponent();
0973: // XXX #6480841 jdk transfering the focus to JRootPane when invoked popup.
0974: if (oppositeComponent instanceof JRootPane) {
0975: oppositeComponent.addFocusListener(this );
0976: }
0977: }
0978: }
0979: } // End of AttributeInlineEditorFocusListener.
0980:
0981: @Override
0982: public boolean isEdited(CssBox box) {
0983: // XXX #94103 Comparing exactly against the text node edited.
0984: // For now checked against textual boxes (TextBox, SpaceBox) only,
0985: // that should be enough.
0986: Node boxText;
0987: if (box instanceof TextBox) {
0988: boxText = ((TextBox) box).getNode();
0989: } else if (box instanceof SpaceBox) {
0990: boxText = ((SpaceBox) box).getNode();
0991: } else {
0992: return false;
0993: }
0994:
0995: Node text = inlineEditorSupport.getText();
0996: // XXX text is source node.
0997: Node editedRenderedText = MarkupService
0998: .getRenderedNodeForNode(text);
0999: if (editedRenderedText == null) {
1000: return false;
1001: }
1002:
1003: while (boxText != null) {
1004: if (boxText == editedRenderedText) {
1005: return true;
1006: }
1007: boxText = boxText.getParentNode();
1008: }
1009: return false;
1010: }
1011: }
|