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.jsf.ui;
0042:
0043: import java.awt.Component;
0044: import java.awt.EventQueue;
0045: import java.awt.Toolkit;
0046: import java.awt.datatransfer.Clipboard;
0047: import java.awt.datatransfer.ClipboardOwner;
0048: import java.awt.datatransfer.Transferable;
0049: import java.awt.dnd.DnDConstants;
0050: import java.awt.event.ActionEvent;
0051: import java.io.IOException;
0052: import java.lang.reflect.InvocationTargetException;
0053: import java.util.Iterator;
0054: import java.util.List;
0055:
0056: import javax.swing.AbstractAction;
0057: import javax.swing.ActionMap;
0058: import javax.swing.FocusManager;
0059: import javax.swing.JComponent;
0060: import javax.swing.SwingUtilities;
0061: import javax.swing.TransferHandler;
0062: import javax.swing.text.DefaultEditorKit;
0063: import javax.swing.text.JTextComponent;
0064: import org.netbeans.modules.visualweb.api.designer.Designer;
0065:
0066: import org.openide.ErrorManager;
0067: import org.openide.awt.UndoRedo;
0068: import org.openide.util.Lookup;
0069: import org.openide.util.Utilities;
0070: import org.openide.util.actions.SystemAction;
0071: import org.openide.util.datatransfer.ClipboardEvent;
0072: import org.openide.util.datatransfer.ClipboardListener;
0073: import org.openide.util.datatransfer.ExClipboard;
0074: import org.openide.util.datatransfer.PasteType;
0075: import org.openide.windows.TopComponent;
0076: import org.w3c.dom.Element;
0077:
0078: import org.netbeans.modules.visualweb.designer.jsf.JsfForm;
0079: import org.netbeans.modules.visualweb.designer.jsf.JsfSupportUtilities;
0080: import org.openide.util.WeakListeners;
0081:
0082: /**
0083: * XXX Before as designer/../SelectionTopComp.
0084: *
0085: * This class provides clipboard and selection functionality for
0086: * a TopComponent such as the design surface or the app outline.
0087: *
0088: * @author Tor Norbye
0089: */
0090: public abstract class AbstractJsfTopComponent extends TopComponent
0091: implements ClipboardOwner {
0092: // protected transient WebForm webform = null;
0093:
0094: /////////////////////////////////////////////////////////////////////////
0095: // Cut, Copy, Paste, Delete support
0096: /////////////////////////////////////////////////////////////////////////
0097: // A lot of this is based on code in openide's ExplorerActions.java
0098: // and ExplorerActionsImpl.java files
0099:
0100: /** copy action performer */
0101: protected final transient CopyCutActionPerformer copyActionPerformer = new CopyCutActionPerformer(
0102: true);
0103:
0104: /** cut action performer */
0105: protected final transient CopyCutActionPerformer cutActionPerformer = new CopyCutActionPerformer(
0106: false);
0107:
0108: /** delete action performer */
0109: protected final transient DeleteActionPerformer deleteActionPerformer = new DeleteActionPerformer();
0110:
0111: /** Paste action performer. */
0112: private final transient OwnPaste pasteActionPerformer = new OwnPaste();
0113:
0114: /** tracker for all actions */
0115: private CBListener cblistener;
0116:
0117: // protected AbstractJsfTopComponent() {
0118: // }
0119:
0120: protected final JsfForm jsfForm;
0121: protected final Designer designer;
0122:
0123: public AbstractJsfTopComponent(/*WebForm webform*/JsfForm jsfForm,
0124: Designer designer) {
0125: // this.webform = webform;
0126: this .jsfForm = jsfForm;
0127: this .designer = designer;
0128: }
0129:
0130: // public WebForm getWebForm() {
0131: // return webform;
0132: // }
0133:
0134: /* Activates copy/cut/paste actions.
0135: */
0136: // @Override
0137: // protected void componentActivated() {
0138: protected void designerActivated() {
0139: // super.componentActivated();
0140:
0141: // //Log.err.log("Component activated!");
0142: // if (cblistener == null) {
0143: // cblistener = new CBListener();
0144: // }
0145: // #104330 Using weak listener.
0146: // XXX Is this efficient to recreate it each time the comp is activated?
0147: cblistener = new CBListener();
0148: Clipboard c = getClipboard();
0149: if (c instanceof ExClipboard) {
0150: ExClipboard clip = (ExClipboard) c;
0151: // clip.addClipboardListener(cblistener);
0152: clip.addClipboardListener(WeakListeners.create(
0153: ClipboardListener.class, cblistener, clip));
0154: }
0155:
0156: // if (webform != null) {
0157: // activateActions();
0158: // }
0159: // activateActions();
0160: updatePasteAction();
0161:
0162: // if (webform != null) {
0163: // webform.getModel().setActivated(true);
0164: // webform.setModelActivated(true);
0165: // }
0166: jsfForm.setModelActivated(true);
0167: }
0168:
0169: /* Deactivates copy/cut/paste actions.
0170: */
0171: // @Override
0172: // protected void componentDeactivated() {
0173: protected void designerDeactivated() {
0174: // if (webform != null) {
0175: //// webform.getModel().setActivated(false);
0176: // webform.setModelActivated(false);
0177: // }
0178: jsfForm.setModelActivated(false);
0179:
0180: // // XXX why not super.componentDeactivated?
0181: // //OutlineTopComp.getInstance().setCurrent(null);
0182: // Clipboard c = getClipboard();
0183: //
0184: // if (c instanceof ExClipboard) {
0185: // ExClipboard clip = (ExClipboard)c;
0186: // clip.removeClipboardListener(cblistener);
0187: // }
0188: // XXX Removing the weak listener.
0189: cblistener = null;
0190:
0191: // if (webform != null) {
0192: // deactivateActions();
0193: // }
0194: deactivateActions();
0195:
0196: // Just leave the nodes visible until some other window
0197: // sets the activation - that way, the user can for example
0198: // move up to the main window and not loose sight of the
0199: // currently selected component
0200: }
0201:
0202: // protected abstract boolean isSelectionEmpty();
0203:
0204: protected abstract void deleteSelection();
0205:
0206: protected abstract Transferable copy();
0207:
0208: // protected abstract DesignBean getPasteParent();
0209: protected abstract Element getPasteParentComponent();
0210:
0211: // /**
0212: // * This method returns the position at which a component should be
0213: // * pasted.
0214: // * <p>
0215: // * This implementation returns the current position of the mouse cursor.
0216: // * If the cursor is outside the visual editor or hasn't moved
0217: // * since the last time this method was invoked, this method will
0218: // * return a position one grid box below and to the right of the
0219: // * currently selected component. If no component is selected, this method
0220: // * returns <code>null</code>.
0221: // *
0222: // * @return the point at which to paste a component or <code>null</code> if
0223: // * there is no valid paste position
0224: // */
0225: // public /*protected*/ Point getPastePosition() {
0226: //// Point p = webform.getManager().getMouseHandler().getCurrentPos();
0227: // Point p = designer.getCurrentPos();
0228: //
0229: // if (p != null) {
0230: // // Ensure that if user pastes multiple times without moving
0231: // // the mouse, we don't reuse the mouse position but switch
0232: // // to an offset from selection instead
0233: // Point location = new Point(p);
0234: //// webform.getManager().getMouseHandler().clearCurrentPos();
0235: // designer.clearCurrentPos();
0236: //
0237: // return location;
0238: // } else {
0239: //// Element e = webform.getSelection().getPositionElement();
0240: // Element e = designer.getPositionElement();
0241: //
0242: // if (e == null) {
0243: // return null;
0244: // }
0245: //
0246: //// int top = CssLookup.getLength(e, XhtmlCss.TOP_INDEX);
0247: //// int left = CssLookup.getLength(e, XhtmlCss.LEFT_INDEX);
0248: // int top = CssProvider.getValueService().getCssLength(e, XhtmlCss.TOP_INDEX);
0249: // int left = CssProvider.getValueService().getCssLength(e, XhtmlCss.LEFT_INDEX);
0250: //
0251: // if ((top != CssValue.AUTO) || (left != CssValue.AUTO)) {
0252: // if (left == CssValue.AUTO) {
0253: // left = 0;
0254: // }
0255: //
0256: // if (top == CssValue.AUTO) {
0257: // top = 0;
0258: // }
0259: //
0260: //// GridHandler gh = GridHandler.getInstance();
0261: //// GridHandler gh = webform.getGridHandler();
0262: //// return new Point(left + gh.getGridWidth(), top + gh.getGridHeight());
0263: // int gridWidth = designer.getGridWidth();
0264: // int gridHeight = designer.getGridHeight();
0265: // return new Point(left + gridWidth, top + gridHeight);
0266: // }
0267: //
0268: // return null;
0269: // }
0270: // }
0271:
0272: // protected abstract MarkupPosition getPasteMarkupPosition();
0273:
0274: // protected abstract void selectBeans(DesignBean[] beans);
0275: protected abstract void selectComponents(
0276: Element[] coponentRootElements);
0277:
0278: /**
0279: * Do what it takes to show a popup menu at the most natural
0280: * place when the user has pressed e.g. shift-f10. Usually
0281: * you'll want to post the menu right under the primary selection
0282: * item.
0283: */
0284: protected abstract void showKeyboardPopup();
0285:
0286: // /** Called when this window is activated: make cut, copy, paste and delete
0287: // sensitive based on whether or not anything is selected and whether
0288: // the clipboard contains something we can absorb. */
0289: // public void activateActions() {
0290: // if (isSelectionEmpty()) {
0291: // disableCutCopyDelete();
0292: // } else {
0293: // enableCutCopyDelete();
0294: // }
0295: //
0296: // updatePasteAction();
0297: // }
0298:
0299: /** Called when the when the component is deactivated. We no longer
0300: allow our paste types to be invoked so clear it - get rid of
0301: the action performers as well. */
0302: public void deactivateActions() {
0303: // if (paste != null) {
0304: // // TODO This was wrong, you cannot clear paste types, the PasteAction won't work then.
0305: // // You need to follow new approach to provide context for actions like paste,
0306: // // using ActionMap, see how it is used in CloneableEditor and follow that example.
0307: // // FIXME Follow http://www.netbeans.org/project/www/download/dev/javadoc/OpenAPIs/org/openide/util/actions/CallbackSystemAction.html#setActionPerformer(org.openide.util.actions.ActionPerformer)
0308: // // and be aware to change it for cut/copy/delete actions too (all callback actions used).
0309: // // paste.setPasteTypes(null);
0310: // }
0311: }
0312:
0313: // private boolean isActivated() {
0314: // return this == TopComponent.getRegistry().getActivated();
0315: // }
0316:
0317: // /** Called when the selection is non zero and the component is active:
0318: // enable cut, copy and delete */
0319: // public void enableCutCopyDelete() {
0320: // if (!isActivated()) {
0321: // return;
0322: // }
0323: //
0324: // copyActionPerformer.setEnabled(true);
0325: // cutActionPerformer.setEnabled(true);
0326: // deleteActionPerformer.setEnabled(true);
0327: // }
0328: //
0329: // /** Called when the selection is removed: disable copy, cut, delete */
0330: // public void disableCutCopyDelete() {
0331: // if (!isActivated()) {
0332: // return;
0333: // }
0334: //
0335: // copyActionPerformer.setEnabled(false);
0336: // cutActionPerformer.setEnabled(false);
0337: // deleteActionPerformer.setEnabled(false);
0338: // }
0339:
0340: // /** Paste the beans in the given transferable to the given parent
0341: // * and markup position.
0342: // */
0343: //// public static DesignBean[] pasteBeans(WebForm webform, Transferable t, DesignBean parent,
0344: //// MarkupPosition pos, Point location) {
0345: // public static Element[] pasteComponents(WebForm webform, Transferable t, Element parentComponentRootElement, Point location) {
0346: //// // Make sure we're allowed to paste to the given parent.
0347: //// // Arguably, I should not be enabling the paste action when the selected parent
0348: //// // is "selected", but the parent used is computed very dynamically, so
0349: //// // doing something like this would require recomputing the Paste state
0350: //// // every pixel the mouse moves over the designer canvas. Instead we try
0351: //// // to move the parent up until we find a suitable parent.
0352: //// while (parent != null) {
0353: //// DndHandler dndHandler = webform.getPane().getDndHandler();
0354: //// int allowed = dndHandler.computeActions(parent, t, false, DropSupport.CENTER);
0355: ////
0356: //// if ((allowed & DnDConstants.ACTION_COPY_OR_MOVE) != 0) {
0357: //// break;
0358: //// }
0359: ////
0360: //// parent = parent.getBeanParent();
0361: //// pos = null; // no longer valid - just use insync defaults
0362: //// }
0363: ////
0364: //// if (parent == null) {
0365: //// // No valid parent found.
0366: //// Toolkit.getDefaultToolkit().beep();
0367: ////
0368: //// return null;
0369: //// }
0370: ////
0371: //// Document document = null;
0372: ////
0373: //// //LiveUnit unit = (LiveUnit)parent.getDesignContext();
0374: //// LiveUnit unit = webform.getModel().getLiveUnit();
0375: ////
0376: //// String description = NbBundle.getMessage(SelectionTopComp.class, "Paste"); // NOI18N
0377: //// UndoEvent undoEvent = webform.getModel().writeLock(description);
0378: //// try {
0379: ////// document = webform.getDocument();
0380: //////
0381: ////// //document.setAutoIgnore(true);
0382: ////// String description = NbBundle.getMessage(SelectionTopComp.class, "Paste"); // NOI18N
0383: ////// document.writeLock(description);
0384: ////
0385: //// DesignBean[] beans = unit.pasteBeans(t, parent, pos);
0386: ////
0387: //// if (beans == null) {
0388: //// return null;
0389: //// }
0390: ////
0391: //// // Decide whether we need to strip out position coordinates
0392: //// // from the beans being moved
0393: //// boolean needPos = true;
0394: ////
0395: //// if (parent != null) {
0396: //// needPos = DndHandler.isGridContext(parent, pos);
0397: ////
0398: //// if (!needPos) {
0399: //// location = null;
0400: //// }
0401: //// }
0402: ////
0403: //// // Determine if the destination is a grid area
0404: //// if (location != null) {
0405: //// // Snap
0406: //// GridHandler gh = GridHandler.getInstance();
0407: ////
0408: //// if (gh.snap()) {
0409: //// // TODO - compute the right target box here
0410: //// CssBox gridBox = null;
0411: //// location.x = gh.snapX(location.x, gridBox);
0412: //// location.y = gh.snapY(location.y, gridBox);
0413: //// }
0414: ////
0415: //// // Position elements
0416: //// Point topLeft = getTopLeft(beans);
0417: ////
0418: //// for (int i = 0; i < beans.length; i++) {
0419: //// if (!(beans[i] instanceof MarkupDesignBean)) {
0420: //// continue;
0421: //// }
0422: ////
0423: //// MarkupDesignBean bean = (MarkupDesignBean)beans[i];
0424: ////
0425: //// // XXX I need to do this on the -rendered- element!
0426: //// Element element = bean.getElement();
0427: //// assert element != null;
0428: ////
0429: //// try {
0430: ////// webform.getDomSynchronizer().setUpdatesSuspended(bean, true);
0431: //// webform.setUpdatesSuspended(bean, true);
0432: ////
0433: //// if (!needPos) {
0434: ////// XhtmlCssEngine engine = webform.getMarkup().getCssEngine();
0435: //// List remove = new ArrayList(5);
0436: //// remove.add(new StyleData(XhtmlCss.POSITION_INDEX));
0437: //// remove.add(new StyleData(XhtmlCss.LEFT_INDEX));
0438: //// remove.add(new StyleData(XhtmlCss.TOP_INDEX));
0439: //// remove.add(new StyleData(XhtmlCss.RIGHT_INDEX));
0440: //// remove.add(new StyleData(XhtmlCss.BOTTOM_INDEX));
0441: ////// <removing design bean manipulation in engine>
0442: ////// engine.updateLocalStyleValues((RaveElement)element, null, remove);
0443: ////// ====
0444: //// CssProvider.getEngineService().updateLocalStyleValuesForElement(element,
0445: //// null, (StyleData[])remove.toArray(new StyleData[remove.size()]));
0446: ////// </removing design bean manipulation in engine>
0447: ////
0448: //// continue;
0449: //// }
0450: ////
0451: //// List set = new ArrayList(5);
0452: //// List remove = new ArrayList(5);
0453: ////// Value val = CssLookup.getValue(element, XhtmlCss.POSITION_INDEX);
0454: //// CssValue cssValue = CssProvider.getEngineService().getComputedValueForElement(element, XhtmlCss.POSITION_INDEX);
0455: ////
0456: ////// if ((val == CssValueConstants.ABSOLUTE_VALUE) ||
0457: ////// (val == CssValueConstants.RELATIVE_VALUE) ||
0458: ////// (val == CssValueConstants.FIXED_VALUE)) {
0459: //// if (CssProvider.getValueService().isAbsoluteValue(cssValue)
0460: //// || CssProvider.getValueService().isRelativeValue(cssValue)
0461: //// || CssProvider.getValueService().isFixedValue(cssValue)) {
0462: ////// int top = CssLookup.getLength(element, XhtmlCss.TOP_INDEX);
0463: ////// int left = CssLookup.getLength(element, XhtmlCss.LEFT_INDEX);
0464: //// int top = CssUtilities.getCssLength(element, XhtmlCss.TOP_INDEX);
0465: //// int left = CssUtilities.getCssLength(element, XhtmlCss.LEFT_INDEX);
0466: ////
0467: //// if ((top != CssBox.AUTO) || (left != CssBox.AUTO)) {
0468: //// if (left == CssBox.AUTO) {
0469: //// left = 0;
0470: //// }
0471: ////
0472: //// if (top == CssBox.AUTO) {
0473: //// top = 0;
0474: //// }
0475: ////
0476: //// left = (location.x + left) - topLeft.x;
0477: //// top = (location.y + top) - topLeft.y;
0478: ////
0479: //// set.add(new StyleData(XhtmlCss.TOP_INDEX,
0480: //// Integer.toString(top) + "px")); // NOI18N
0481: //// set.add(new StyleData(XhtmlCss.LEFT_INDEX,
0482: //// Integer.toString(left) + "px")); // NOI18N
0483: //// } else {
0484: //// set.add(new StyleData(XhtmlCss.LEFT_INDEX,
0485: //// Integer.toString(location.x) + "px")); // NOI18N
0486: //// set.add(new StyleData(XhtmlCss.TOP_INDEX,
0487: //// Integer.toString(location.y) + "px")); // NOI18N
0488: //// }
0489: //// } else {
0490: //// set.add(new StyleData(XhtmlCss.POSITION_INDEX,
0491: ////// CssConstants.CSS_ABSOLUTE_VALUE)); // NOI18N
0492: //// CssProvider.getValueService().getAbsoluteValue()));
0493: //// set.add(new StyleData(XhtmlCss.LEFT_INDEX,
0494: //// Integer.toString(location.x) + "px")); // NOI18N
0495: //// set.add(new StyleData(XhtmlCss.TOP_INDEX,
0496: //// Integer.toString(location.y) + "px")); // NOI18N
0497: //// }
0498: ////
0499: //// remove.add(new StyleData(XhtmlCss.RIGHT_INDEX));
0500: //// remove.add(new StyleData(XhtmlCss.BOTTOM_INDEX));
0501: ////
0502: ////// XhtmlCssEngine engine = webform.getMarkup().getCssEngine();
0503: ////// <removing design bean manipulation in engine>
0504: ////// engine.updateLocalStyleValues((RaveElement)element, set, remove);
0505: ////// ====
0506: //// CssProvider.getEngineService().updateLocalStyleValuesForElement(element,
0507: //// (StyleData[])set.toArray(new StyleData[set.size()]),
0508: //// (StyleData[])remove.toArray(new StyleData[remove.size()]));
0509: ////// </removing design bean manipulation in engine>
0510: //// } finally {
0511: ////// webform.getDomSynchronizer().setUpdatesSuspended(bean, false);
0512: //// webform.setUpdatesSuspended(bean, false);
0513: //// }
0514: //// }
0515: //// } else if (needPos) {
0516: //// // We're over a grid area but don't have a specified position;
0517: //// // leave existing positions in the pasted components alone
0518: //// // but don't create new positions to assign to other components.
0519: //// // This means that if you cut a component and then paste it
0520: //// // it will appear in the place it was before cutting it.
0521: //// } else {
0522: //// // Flow area: remove absolute positions for all children
0523: //// for (int i = 0; i < beans.length; i++) {
0524: //// if (!(beans[i] instanceof MarkupDesignBean)) {
0525: //// // Not a visual component
0526: //// continue;
0527: //// }
0528: ////
0529: //// MarkupDesignBean bean = (MarkupDesignBean)beans[i];
0530: //// Element element = bean.getElement();
0531: ////
0532: //// try {
0533: ////// webform.getDomSynchronizer().setUpdatesSuspended(bean, true);
0534: //// webform.setUpdatesSuspended(bean, true);
0535: ////// CssLookup.removeLocalStyleValue(element, XhtmlCss.POSITION_INDEX);
0536: ////// CssLookup.removeLocalStyleValue(element, XhtmlCss.LEFT_INDEX);
0537: ////// CssLookup.removeLocalStyleValue(element, XhtmlCss.TOP_INDEX);
0538: //// CssProvider.getEngineService().removeLocalStyleValueForElement(element, XhtmlCss.POSITION_INDEX);
0539: //// CssProvider.getEngineService().removeLocalStyleValueForElement(element, XhtmlCss.LEFT_INDEX);
0540: //// CssProvider.getEngineService().removeLocalStyleValueForElement(element, XhtmlCss.TOP_INDEX);
0541: //// } finally {
0542: ////// webform.getDomSynchronizer().setUpdatesSuspended(bean, false);
0543: //// webform.setUpdatesSuspended(bean, false);
0544: //// }
0545: //// }
0546: //// }
0547: ////
0548: //// return beans;
0549: //// } finally {
0550: ////// document.writeUnlock();
0551: //// webform.getModel().writeUnlock(undoEvent);
0552: //// }
0553: //
0554: //// return webform.pasteBeans(t, parent, pos, location, GridHandler.getInstance());
0555: //// return webform.pasteBeans(t, parent, pos, location, webform.getGridHandler());
0556: // return webform.pasteComponents(t, parentComponentRootElement, location, webform.getGridHandler());
0557: // }
0558:
0559: // /** Compute the leftmost and topmost positions among the given beans
0560: // */
0561: // private static Point getTopLeft(DesignBean[] beans) {
0562: // int minLeft = Integer.MAX_VALUE;
0563: // int minTop = Integer.MAX_VALUE;
0564: //
0565: // for (int i = 0; i < beans.length; i++) {
0566: // Element element = FacesSupport.getElement(beans[i]);
0567: //
0568: // if (element == null) {
0569: // // Not a visual component
0570: // continue;
0571: // }
0572: //
0573: //// Value val = CssLookup.getValue(element, XhtmlCss.POSITION_INDEX);
0574: // CssValue cssValue = CssProvider.getEngineService().getComputedValueForElement(element, XhtmlCss.POSITION_INDEX);
0575: //
0576: //// if ((val == CssValueConstants.ABSOLUTE_VALUE) ||
0577: //// (val == CssValueConstants.RELATIVE_VALUE) ||
0578: //// (val == CssValueConstants.FIXED_VALUE)) {
0579: // if (CssProvider.getValueService().isAbsoluteValue(cssValue)
0580: // || CssProvider.getValueService().isRelativeValue(cssValue)
0581: // || CssProvider.getValueService().isFixedValue(cssValue)) {
0582: //// int top = CssLookup.getLength(element, XhtmlCss.TOP_INDEX);
0583: //// int left = CssLookup.getLength(element, XhtmlCss.LEFT_INDEX);
0584: // int top = CssUtilities.getCssLength(element, XhtmlCss.TOP_INDEX);
0585: // int left = CssUtilities.getCssLength(element, XhtmlCss.LEFT_INDEX);
0586: //
0587: // if ((top != CssBox.AUTO) || (left != CssBox.AUTO)) {
0588: // if (left == CssBox.AUTO) {
0589: // left = 0;
0590: // }
0591: //
0592: // if (top == CssBox.AUTO) {
0593: // top = 0;
0594: // }
0595: //
0596: // if (top < minTop) {
0597: // minTop = top;
0598: // }
0599: //
0600: // if (left < minLeft) {
0601: // minLeft = left;
0602: // }
0603: // }
0604: // }
0605: // }
0606: //
0607: // return new Point(minLeft, minTop);
0608: // }
0609:
0610: // private static final DataFlavor FLAVOR_DISPLAY_ITEM = new DataFlavor(
0611: // DataFlavor.javaJVMLocalObjectMimeType + "; class=" + DisplayItem.class.getName(), // NOI18N
0612: // "RAVE_PALETTE_ITEM"); // TODO get rid of such name.
0613:
0614: /** Updates paste action.
0615: * @param path selected nodes
0616: */
0617: /*private*/void updatePasteAction() {
0618: Clipboard clipboard = getClipboard();
0619: Transferable trans = clipboard.getContents(this );
0620:
0621: // if (trans != null) {
0622: // DataFlavor[] df = trans.getTransferDataFlavors();
0623: // int n = 0;
0624: //
0625: // if (df != null) {
0626: // n = df.length;
0627: // }
0628: //
0629: // for (int i = 0; i < n; i++) {
0630: // DataFlavor flavor = df[i];
0631: //
0632: // // XXX TODO Get rid of this dep, you can specify your own data flavor
0633: // // which can match, there will be created new data flavors avoiding
0634: // // usage of .
0635: // if (FLAVOR_DISPLAY_ITEM.equals(flavor)
0636: // || (flavor.getRepresentationClass() == String.class)
0637: // || flavor.getMimeType().startsWith("application/x-creator-")) { // NOI18N
0638: //
0639: // // Yes!
0640: // PasteType[] pasteTypes = new PasteType[] { new Paste() };
0641: // pasteActionPerformer.setPasteTypes(pasteTypes);
0642: //
0643: // return;
0644: // }
0645: // }
0646: // }
0647: // if (webform.canPasteTransferable(trans)) {
0648: // if (jsfForm.canPasteTransferable(trans)) {
0649: if (jsfForm.canPasteTransferable(designer
0650: .getSelectedComponents(), trans)) {
0651: // Yes!
0652: PasteType[] pasteTypes = new PasteType[] { new Paste() };
0653: pasteActionPerformer.setPasteTypes(pasteTypes);
0654: return;
0655: }
0656:
0657: pasteActionPerformer.setPasteTypes(null);
0658: }
0659:
0660: // Implements ClipboardOwner
0661: public void lostOwnership(Clipboard clipboard, Transferable contents) {
0662: }
0663:
0664: /** If our clipboard is not found return the default system
0665: clipboard. */
0666: private static Clipboard getClipboard() {
0667: Clipboard c = (Clipboard) Lookup.getDefault().lookup(
0668: Clipboard.class);
0669:
0670: if (c == null) {
0671: c = Toolkit.getDefaultToolkit().getSystemClipboard();
0672: }
0673:
0674: return c;
0675: }
0676:
0677: @Override
0678: public UndoRedo getUndoRedo() {
0679: // if (webform != null) {
0680: //// return webform.getModel().getUndoManager();
0681: // return webform.getUndoManager();
0682: // }
0683: //
0684: // return super.getUndoRedo();
0685: return jsfForm.isValid() ? jsfForm.getUndoManager()
0686: : UndoRedo.NONE;
0687: }
0688:
0689: /** Remove any items from the list that are children of any other
0690: * components in the list.
0691: * This is a slow implementation (n^3) but n will always (in real
0692: * scenarios be small).
0693: */
0694: // protected void removeChildrenBeans(ArrayList list) {
0695: // Iterator it = list.listIterator();
0696: //
0697: // while (it.hasNext()) {
0698: // DesignBean lb = (DesignBean)it.next();
0699: //
0700: // // See if any other item in this list is a parent
0701: // for (int i = 0, n = list.size(); i < n; i++) {
0702: // DesignBean parent = (DesignBean)list.get(i);
0703: //
0704: // if ((lb != parent) && isBelow(parent, lb)) {
0705: // it.remove();
0706: //
0707: // break;
0708: // }
0709: // }
0710: // }
0711: // }
0712: protected void removeChildrenComponents(List<Element> list) {
0713: Iterator<Element> it = list.listIterator();
0714:
0715: while (it.hasNext()) {
0716: // DesignBean lb = (DesignBean)it.next();
0717: Element element = it.next();
0718:
0719: // See if any other item in this list is a parent
0720: // for (int i = 0, n = list.size(); i < n; i++) {
0721: // DesignBean parent = (DesignBean)list.get(i);
0722: //
0723: // if ((lb != parent) && isBelow(parent, lb)) {
0724: // it.remove();
0725: //
0726: // break;
0727: // }
0728: // }
0729: for (Element parent : list) {
0730: if (element != parent && isBelow(parent, element)) {
0731: it.remove();
0732:
0733: break;
0734: }
0735: }
0736: }
0737: }
0738:
0739: /** Determine if the given bean is below the given other potential parent */
0740: // private static boolean isBelow(DesignBean parent, DesignBean bean) {
0741: private static boolean isBelow(Element parent, Element element) {
0742: if (element == null) {
0743: return false;
0744: } else if (element == parent) {
0745: return true;
0746: } else {
0747: // return isBelow(parent, WebForm.getDomProviderService().getParentComponent(element));
0748: return isBelow(parent, JsfSupportUtilities
0749: .getParentComponent(element));
0750: }
0751: }
0752:
0753: protected void installActions() {
0754: ActionMap map = getActionMap();
0755:
0756: map.put(DefaultEditorKit.copyAction, copyActionPerformer);
0757: map.put(DefaultEditorKit.cutAction, cutActionPerformer);
0758:
0759: map.put("delete", deleteActionPerformer); // or false
0760:
0761: map.put(DefaultEditorKit.pasteAction, pasteActionPerformer);
0762:
0763: // Popup menu from the keyboard
0764: map.put("org.openide.actions.PopupAction",
0765: new AbstractAction() {
0766: public void actionPerformed(ActionEvent evt) {
0767: SwingUtilities.invokeLater(new Runnable() {
0768: public void run() {
0769: showKeyboardPopup();
0770: }
0771: });
0772: }
0773: });
0774: }
0775:
0776: /** Own implementation of paste action. */
0777: private static class OwnPaste extends AbstractAction {
0778: private PasteType[] pasteTypes;
0779:
0780: private OwnPaste() {
0781: }
0782:
0783: public boolean isEnabled() {
0784: // updateActionsState();
0785: return super .isEnabled();
0786: }
0787:
0788: public void setPasteTypes(PasteType[] arr) {
0789: synchronized (this ) {
0790: this .pasteTypes = arr;
0791: }
0792:
0793: setEnabled(arr != null);
0794: }
0795:
0796: public void actionPerformed(ActionEvent e) {
0797: throw new IllegalStateException(
0798: "Should not be invoked at all. Paste types: "
0799: + java.util.Arrays.asList(pasteTypes)); // NOI18N
0800: }
0801:
0802: public Object getValue(String s) {
0803: // updateActionsState();
0804: if ("delegates".equals(s)) { // NOI18N
0805:
0806: return pasteTypes;
0807: }
0808:
0809: return super .getValue(s);
0810: }
0811: }
0812:
0813: private static class Paste extends PasteType {
0814: public Transferable paste() throws IOException {
0815: Clipboard clipboard = getClipboard();
0816: final Transferable t = clipboard.getContents(this );
0817:
0818: if (t != null) {
0819: // XXX TODO This is very suspicious, and calling invokeAndWait very dangerous, investigate later!
0820: // We run into deadlocks without this;
0821: // !#$!@#!@ ModuleActions thread
0822: try {
0823: SwingUtilities.invokeAndWait(new Runnable() {
0824: public void run() {
0825: pasteSynchronous(t);
0826: }
0827: });
0828: } catch (InvocationTargetException ite) {
0829: ErrorManager.getDefault().notify(ite.getCause());
0830: ErrorManager.getDefault().notify(ite);
0831: } catch (InterruptedException ie) {
0832: ErrorManager.getDefault().notify(ie);
0833: }
0834: }
0835:
0836: //This makes this not clear the clipboard
0837: return t;
0838:
0839: // to clear clipboard, return ExTransferable.EMPTY instead...
0840: }
0841:
0842: private void pasteSynchronous(Transferable t) {
0843: // XXX Trying to fix the paste type impl, but the default handler doesn't work as expected.
0844: // Needs to have a closer look at it.
0845: Component focusOwner = FocusManager.getCurrentManager()
0846: .getFocusOwner();
0847: if (focusOwner instanceof JTextComponent) {
0848: // Inline editing.
0849: JTextComponent textComponent = (JTextComponent) focusOwner;
0850: textComponent.paste();
0851: } else if (focusOwner instanceof JComponent) {
0852: // DesignerPane.
0853: JComponent component = (JComponent) focusOwner;
0854: TransferHandler th = component.getTransferHandler();
0855: if (th != null) {
0856: th.importData(component, t);
0857: }
0858: }
0859:
0860: // // See if it's a plain String paste
0861: // DataFlavor[] df = t.getTransferDataFlavors();
0862: // int n = 0;
0863: //
0864: // if (df != null) {
0865: // n = df.length;
0866: // }
0867: //
0868: // for (int i = 0; i < n; i++) {
0869: // DataFlavor flavor = df[i];
0870: //
0871: // if (FLAVOR_DISPLAY_ITEM.equals(flavor)
0872: // || flavor.getMimeType().startsWith("application/x-creator-")) { // NOI18N
0873: //
0874: // DesignBean parent = getPasteParent();
0875: // MarkupPosition pos = getPasteMarkupPosition();
0876: // Point location = getPastePosition();
0877: // DesignBean[] beans = pasteBeans(webform, t, parent, pos, location);
0878: //
0879: // if ((beans != null) && (beans.length > 0)) {
0880: // selectBeans(beans);
0881: // }
0882: //
0883: // return;
0884: // } else if (flavor.getRepresentationClass() == String.class) {
0885: // try {
0886: // String content = (String)t.getTransferData(flavor);
0887: //
0888: // // XXX #6332049 When in inline editing we shouldn't steal the paste
0889: // // (at least for the JTextComponent's.
0890: // // This is just a workaround, it shouldn't be done this way.
0891: // // actions should be created based on context (and inline editing
0892: // // context is diff from the designer pane one).
0893: // if(webform.getManager().isInlineEditing()) {
0894: // Component comp = FocusManager.getCurrentManager().getFocusOwner();
0895: // if(comp instanceof JTextComponent) {
0896: // JTextComponent textComp = (JTextComponent)comp;
0897: // textComp.paste();
0898: // return;
0899: // }
0900: // }
0901: //
0902: // if (webform.getPane().getCaret() != null) {
0903: // webform.getPane().getCaret().replaceSelection(content);
0904: // } else {
0905: // Point location = getPastePosition();
0906: // DndHandler handler = webform.getPane().getDndHandler();
0907: //// handler.setDropPoint(location);
0908: ////
0909: //// //handler.setInsertPosition(getPasteMarkupPosition());
0910: //// handler.importString(content);
0911: // handler.importString(content, location, GridHandler.getInstance());
0912: // }
0913: // } catch (Exception ex) {
0914: // ErrorManager.getDefault().notify(ex);
0915: // }
0916: //
0917: // return;
0918: // }
0919: // }
0920: }
0921: }
0922:
0923: /** Class which performs delete action */
0924: class DeleteActionPerformer extends AbstractAction {
0925: public void actionPerformed(final ActionEvent e) {
0926: // XXX #6491546 The delete performer is invoked not in AWT thread.
0927: if (EventQueue.isDispatchThread()) {
0928: doActionPerformed(e);
0929: } else {
0930: EventQueue.invokeLater(new Runnable() {
0931: public void run() {
0932: doActionPerformed(e);
0933: }
0934: });
0935: }
0936: }
0937:
0938: private void doActionPerformed(ActionEvent e) {
0939: // XXX #6333143 When in inline editing, delegate to inline editor.
0940: // if(webform.getManager().isInlineEditing()) {
0941: // webform.getManager().getInlineEditor().invokeDeleteNextCharAction(e);
0942: // return;
0943: // }
0944: if (designer.isInlineEditing()) {
0945: designer.invokeDeleteNextCharAction(e);
0946: return;
0947: }
0948:
0949: if (Utilities.getOperatingSystem() == Utilities.OS_MAC) {
0950: // On the mac only, the delete key in the designer causes
0951: // a DeleteAction to be performed. During inline editing
0952: // this cancels us out and -deletes the component- which
0953: // is really bad. By ensuring that the source is the top
0954: // component I allow users to still delete the inlined edited
0955: // component from the toolbar delete button or the context menu.
0956: // TODO: file NetBeans platform bug
0957: // if (webform.getManager().isInlineEditing() &&
0958: // (e.getSource() == webform.getTopComponent())) {
0959: if (designer.isInlineEditing()
0960: && (e.getSource() == this )) {
0961: // The delete event is also received by the delete forward action
0962: return;
0963: }
0964: }
0965:
0966: performAction(null);
0967: }
0968:
0969: /** Perform delete action. */
0970: public void performAction(SystemAction action) {
0971: // We run into deadlocks without this; !#$!@#!@ ModuleActions thread
0972: SwingUtilities.invokeLater(new Runnable() {
0973: public void run() {
0974: // webform.getManager().finishInlineEditing(false);
0975: designer.finishInlineEditing(false);
0976: deleteSelection();
0977: }
0978: });
0979: }
0980: }
0981:
0982: // // XXX Moved from formder designer/outline/DropSupport.
0983: // private static final int DROP_ABOVE = -1;
0984: // private static final int DROP_CENTER = 0;
0985: // private static final int DROP_BELOW = 1;
0986:
0987: /** Class which performs copy and cut actions */
0988: class CopyCutActionPerformer extends AbstractAction {
0989: /** determine if adapter is used for copy or cut action. */
0990: boolean isCopy;
0991:
0992: /** Create new adapter */
0993: public CopyCutActionPerformer(boolean b) {
0994: isCopy = b;
0995: }
0996:
0997: public void actionPerformed(ActionEvent e) {
0998: performAction(null);
0999: }
1000:
1001: /** Perform copy or cut action. */
1002: public void performAction(SystemAction action) {
1003: // if (webform.getManager().isInlineEditing()) {
1004: if (designer.isInlineEditing()) {
1005: // Transferable t = webform.getManager().getInlineEditor().copyText(!isCopy);
1006: Transferable t = designer.inlineCopyText(!isCopy);
1007:
1008: if (t != null) {
1009: // getClipboard().setContents(t, SelectionTopComp.this);
1010: getClipboard().setContents(t,
1011: AbstractJsfTopComponent.this );
1012:
1013: return;
1014: }
1015: }
1016:
1017: Transferable t = copy();
1018: // XXX Happened NPE.
1019: // FIXME Why was this performer enabled?
1020: if (t == null) {
1021: return;
1022: }
1023:
1024: // XXX #110353 Incorrect check for allowing cut/copy.
1025: // boolean pastable = false;
1026: //
1027: // //Check if a non-pastable component is selected, if so, pretend nothing was selected
1028: //// DndHandler dndHandler = webform.getPane().getDndHandler();
1029: //
1030: //// for (DesignBean parent = getPasteParent(); parent != null;
1031: //// parent = parent.getBeanParent()) {
1032: // for (Element parentComponentRootElement = getPasteParentComponent(); parentComponentRootElement != null;
1033: //// parentComponentRootElement = WebForm.getDomProviderService().getParentComponent(parentComponentRootElement)) {
1034: // parentComponentRootElement = JsfSupportUtilities.getParentComponent(parentComponentRootElement)) {
1035: //// int allowed = dndHandler.computeActions(parent, t, false, /*DropSupport.CENTER*/DROP_CENTER);
1036: //// int allowed = dndHandler.computeActions(parentComponentRootElement, t, false, /*DropSupport.CENTER*/DROP_CENTER);
1037: //// int allowed = webform.computeActions(parentComponentRootElement, t);
1038: // int allowed = jsfForm.computeActions(parentComponentRootElement, t);
1039: //
1040: // if ((allowed & DnDConstants.ACTION_COPY_OR_MOVE) != 0) {
1041: // pastable = true;
1042: //
1043: // break;
1044: // }
1045: // }
1046:
1047: // if ((t != null) && pastable) {
1048: if (t != null) {
1049: // XXX why the string selection??
1050: // getClipboard().setContents(t, SelectionTopComp.this);
1051: getClipboard().setContents(t,
1052: AbstractJsfTopComponent.this );
1053:
1054: if (!isCopy) { // cut: we've copied, so now delete...
1055:
1056: // We run into deadlocks without this;
1057: // !#$!@#!@ ModuleActions thread
1058: SwingUtilities.invokeLater(new Runnable() {
1059: public void run() {
1060: // webform.getManager().finishInlineEditing(false);
1061: designer.finishInlineEditing(false);
1062: deleteSelection();
1063: }
1064: });
1065: }
1066: }
1067: }
1068: }
1069:
1070: private class CBListener implements ClipboardListener {
1071: /** This method is called when content of clipboard is changed.
1072: * @param ev event describing the action
1073: */
1074: public void clipboardChanged(ClipboardEvent ev) {
1075: if (!ev.isConsumed()) {
1076: // We can only do this from the event thread
1077: if (SwingUtilities.isEventDispatchThread()) {
1078: updatePasteAction();
1079: } else {
1080: SwingUtilities.invokeLater(new Runnable() {
1081: public void run() {
1082: updatePasteAction();
1083: }
1084: });
1085: }
1086: }
1087: }
1088: }
1089: }
|