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:
0042: package org.netbeans.modules.visualweb.designer;
0043:
0044: import java.awt.AlphaComposite;
0045: import java.awt.BasicStroke;
0046: import java.awt.Color;
0047: import java.awt.Composite;
0048: import java.awt.Cursor;
0049: import java.awt.Graphics;
0050: import java.awt.Graphics2D;
0051: import java.awt.Image;
0052: import java.awt.Point;
0053: import java.awt.Rectangle;
0054: import java.awt.Stroke;
0055: import java.awt.event.InputEvent;
0056: import java.awt.event.KeyEvent;
0057: import java.awt.event.KeyListener;
0058: import java.awt.event.MouseEvent;
0059: import java.awt.geom.AffineTransform;
0060: import java.awt.image.BufferedImage;
0061: import java.util.ArrayList;
0062: import java.util.List;
0063: import javax.swing.ImageIcon;
0064:
0065: import org.netbeans.modules.visualweb.api.designer.DomProvider.DomPosition;
0066: import org.netbeans.modules.visualweb.css2.CssBox;
0067: import org.netbeans.modules.visualweb.css2.ModelViewMapper;
0068: import org.netbeans.modules.visualweb.css2.PageBox;
0069: import org.openide.ErrorManager;
0070:
0071: import org.w3c.dom.Element;
0072: import org.w3c.dom.Node;
0073:
0074: /**
0075: * Handle drawing (and "undrawing"!) a drag outline, as well as
0076: * communicating a drag to the GridHandler on completion
0077: * @todo Should I consult Toolkit.isDynamicLayoutSet() to decide
0078: * if I should do live-image-dragging or just show outlines?
0079: * If users have set outlines only I assume it's not a beefy
0080: * system so could benefit from the simplification.
0081: *
0082: * @author Tor Norbye
0083: */
0084: public class Dragger extends Interaction implements KeyListener {
0085: /** Pixel distance you must drag before dragging will actually
0086: * be performed.
0087: */
0088: private static final int THRESHOLD = 2;
0089:
0090: //private static final boolean DISPLAY_IMAGES = (System.getProperty("rave.enableImageDrags") != null);
0091: private static final boolean DISPLAY_IMAGES = !(System
0092: .getProperty("rave.disableImageDrags") != null);
0093: private static final int DRAG_GRID = 0;
0094: private static final int DRAG_FREE = 1;
0095: private static final int LINKING = 2;
0096:
0097: /* Restore previous cursor after operation. */
0098: protected transient Cursor previousCursor;
0099: private WebForm webform;
0100: private List<CssBox> boxes;
0101: private List<Rectangle> selections;
0102: // private List<MarkupDesignBean> beans;
0103: private Element[] componentRootElements;
0104: private List<Image> images;
0105:
0106: // private Position pos;
0107: private DomPosition pos;
0108:
0109: private int prevX = -500;
0110: private int prevY = -500;
0111: private int prevAction;
0112: private int startX;
0113: private int startY;
0114: private int prevMouseX = -500;
0115: private int prevMouseY = -500;
0116: private int action;
0117: private BasicStroke linkStroke;
0118: private boolean alreadyMoved = false;
0119: private AffineTransform transform;
0120: private AlphaComposite alpha;
0121:
0122: /**
0123: * Create a dragger which tracks a dragging outline offset by the
0124: * given amount from the mouse position, with the given dimension
0125: * @param webform The webform dragging is occurring on
0126: * @param boxes List of boxes being dragged.
0127: * @param selections List of Rectangle objects, where each rectangle
0128: * represents a component getting dragged. The (x,y) position of
0129: * the rectangle represents the relative distance to the rectangle
0130: * from the mouse pointer. The (width,height) of the rectangle
0131: * represents the size of the rectangle to draw.
0132: * Therefore, { [-10,-10,20,30],[30,40,100,100] } means that
0133: * the dragger will draw two selection rectangles as the mouse
0134: * is moving; one of size (20,30), one of size (100,100).
0135: * The first one will be positioned 10 pixels above and to the
0136: * left of the current mouse position, the other one 30 pixels
0137: * to the right and 40 pixels below the cursor.
0138: * <b>Important note</b>: The offset rectangle indicates
0139: * the <b>border</b> edge of the component (as is done in most places
0140: * in the designer - e.g. see CssBox.getAbsoluteX(), getWidth, etc.)
0141: * @param beans List of DesignBean objects corresponding to components moved
0142: * <p>
0143: * If one item in the selection is considered "primary", it should
0144: * be the first item in the list. It is the coordinate of this
0145: * (first) item that is reported in the status bar.
0146: * <p>
0147: * The list of boxes and the list of selections should have
0148: * identical size, and each view corresponds to the rectangle
0149: * in the same position in the other list.
0150: * <p>
0151: * selections may not be null, and may not an empty list.
0152: */
0153: // public Dragger(WebForm webform, List<CssBox> boxes, List<Rectangle> selections, List<MarkupDesignBean> beans) {
0154: public Dragger(WebForm webform, List<CssBox> boxes,
0155: List<Rectangle> selections, Element[] componentRootElements) {
0156: if (DesignerUtils.DEBUG) {
0157: DesignerUtils.debugLog(getClass().getName() + "()");
0158: }
0159: if (webform == null) {
0160: throw (new IllegalArgumentException("Null webform."));
0161: }
0162: if (boxes == null) {
0163: throw (new IllegalArgumentException("Null boxes list."));
0164: }
0165: if (selections == null) {
0166: throw (new IllegalArgumentException("Null selections list."));
0167: }
0168: // if(beans == null) {
0169: // throw(new IllegalArgumentException("Null beans list."));
0170: // }
0171: if (componentRootElements == null) {
0172: throw new IllegalArgumentException(
0173: "Null componentRootElements list."); // NOI18N
0174: }
0175: this .webform = webform;
0176: this .boxes = boxes;
0177: this .selections = selections;
0178: // this.beans = beans;
0179: this .componentRootElements = componentRootElements;
0180: }
0181:
0182: private void cleanup(DesignerPane pane, int oldAction) {
0183: // Restore the cursor to normal
0184: pane.setCursor(previousCursor);
0185:
0186: if (webform.isGridMode()) {
0187: webform.getPane().hideCaret();
0188: }
0189:
0190: // Restore status line
0191: // StatusDisplayer_RAVE.getRaveDefault().clearPositionLabel();
0192:
0193: // GridHandler gm = GridHandler.getInstance();
0194: // GridHandler gm = webform.getGridHandler();
0195: // DesignerPane.clearDirty();
0196: pane.clearDirty();
0197:
0198: if (selections == null) {
0199: pane.repaint(); // XXX why are we doing this?
0200:
0201: return;
0202: }
0203:
0204: int n = selections.size();
0205:
0206: if (oldAction == LINKING) {
0207: // DesignerPane.addDirtyPoint(startX-1, startY-1);
0208: // DesignerPane.addDirtyPoint(startX+1, startY+1);
0209: // DesignerPane.addDirtyPoint(prevX-1, prevY-1);
0210: // DesignerPane.addDirtyPoint(prevX+1, prevY+1);
0211: pane.addDirtyPoint(startX - 1, startY - 1);
0212: pane.addDirtyPoint(startX + 1, startY + 1);
0213: pane.addDirtyPoint(prevX - 1, prevY - 1);
0214: pane.addDirtyPoint(prevX + 1, prevY + 1);
0215: } else {
0216: for (int i = 0; i < n; i++) {
0217: Rectangle r2 = (Rectangle) selections.get(i);
0218: int x;
0219: int y;
0220:
0221: if (oldAction == DRAG_FREE) {
0222: x = r2.x + prevX;
0223: y = r2.y + prevY;
0224: } else if (hasMoved(prevX, prevY)) {
0225: // GridHandler gm = GridHandler.getDefault();
0226: // x = gm.snapX(r2.x + prevX, getPositionedBy(i));
0227: // y = gm.snapY(r2.y + prevY, getPositionedBy(i));
0228: x = webform.snapX(r2.x + prevX, getPositionedBy(i));
0229: y = webform.snapY(r2.y + prevY, getPositionedBy(i));
0230: } else {
0231: x = r2.x + startX;
0232: y = r2.y + startY;
0233: }
0234:
0235: // DesignerPane.addDirtyPoint(x, y);
0236: // DesignerPane.addDirtyPoint(x + r2.width + 1, y + r2.height + 1);
0237: pane.addDirtyPoint(x, y);
0238: pane.addDirtyPoint(x + r2.width + 1, y + r2.height + 1);
0239: }
0240: }
0241:
0242: pane.repaintDirty(false);
0243: }
0244:
0245: /** Cancel operation */
0246: public void cancel(DesignerPane pane) {
0247: pane.removeKeyListener(this );
0248: cleanup(pane, action);
0249: }
0250:
0251: /** When the mouse press is released, get rid of the drawn dragger,
0252: * and ask the selection manager to select all the components contained
0253: * within the dragger bounds.
0254: */
0255: public void mouseReleased(MouseEvent e) {
0256: try {
0257: DesignerPane pane = webform.getPane();
0258: pane.removeKeyListener(this );
0259:
0260: if ((e != null) && !e.isConsumed()) {
0261: int oldAction = action;
0262: updateSnapState(e);
0263:
0264: Point p = e.getPoint();
0265:
0266: cleanup(pane, oldAction);
0267:
0268: if (action == LINKING) {
0269: DndHandler handler = webform.getPane()
0270: .getDndHandler();
0271:
0272: // DesignBean candidate = getLinkParticipant(startX, startY);
0273: // Element candidate = getLinkParticipant(startX, startY);
0274: Element candidate = ModelViewMapper.findElement(
0275: webform.getPane().getPageBox(), startX,
0276: startY);
0277:
0278: if (candidate == null) {
0279: // if (beans.size() > 0) {
0280: if (componentRootElements.length > 0) {
0281: // candidate = (DesignBean)beans.get(0);
0282: candidate = componentRootElements[0];
0283: } else {
0284: handler.clearDropMatch();
0285:
0286: return;
0287: }
0288: }
0289:
0290: // int dropType =
0291: // handler.getDropTypeForClassNamesEx(p,
0292: // new String[] { candidate.getInstance().getClass().getName() },
0293: // new DesignBean[] { candidate }, true);
0294: int dropType = handler.getDropTypeForComponent(p,
0295: candidate, true);
0296:
0297: if (dropType == DndHandler.DROP_LINKED) {
0298: // CssBox box = webform.getMapper().findBox(p.x, p.y);
0299: CssBox box = ModelViewMapper.findBox(webform
0300: .getPane().getPageBox(), p.x, p.y);
0301: Element element = box.getElement();
0302:
0303: if (element != null) {
0304: // ArrayList candidates = new ArrayList();
0305: // candidates.add(candidate);
0306: // handler.processLinks(element, null, candidates, true,
0307: // true, false);
0308: // handler.processLinks(element, null, candidate, true, true, false);
0309: webform.processLinks(element, candidate);
0310: }
0311: } else {
0312: // Try reverse link
0313: // candidate = getLinkParticipant(p.x, p.y);
0314: candidate = ModelViewMapper.findElement(webform
0315: .getPane().getPageBox(), p.x, p.y);
0316:
0317: if (candidate != null) {
0318: // dropType =
0319: // handler.getDropTypeForClassNamesEx(new Point(startX, startY),
0320: // new String[] { candidate.getInstance().getClass().getName() },
0321: // new DesignBean[] { candidate }, true);
0322: dropType = handler.getDropTypeForComponent(
0323: new Point(startX, startY),
0324: candidate, true);
0325: }
0326:
0327: if (dropType == DndHandler.DROP_LINKED) {
0328: // CssBox box = webform.getMapper().findBox(startX, startY);
0329: CssBox box = ModelViewMapper.findBox(
0330: webform.getPane().getPageBox(),
0331: startX, startY);
0332: Element element = box.getElement();
0333:
0334: if (element != null) {
0335: // ArrayList candidates = new ArrayList();
0336: // candidates.add(candidate);
0337:
0338: // handler.processLinks(element, null, candidates, true,
0339: // true, false);
0340: // handler.processLinks(element, null, candidate, true, true, false);
0341: webform
0342: .processLinks(element,
0343: candidate);
0344: }
0345: }
0346: }
0347:
0348: handler.clearDropMatch();
0349: } else if ((p.x != startX) || // Don't use snapped coords here!
0350: (p.y != startY)) {
0351: /*
0352: How do I -actually- move a component in the
0353: document?
0354:
0355: (With CSS2 it's easy - I just update its style
0356: attribute to contain the new coordinates.)
0357:
0358: It must currently be positioned in a layout
0359: table. Thus, this becomes a GridHandler update
0360: call where it needs to recompute its positions.
0361: */
0362: prevX = p.x;
0363: prevY = p.y;
0364:
0365: // GridHandler gm = GridHandler.getInstance();
0366: // GridHandler gm = webform.getGridHandler();
0367: boolean grid = isOverGrid(prevX, prevY);
0368:
0369: if (grid) {
0370: // pos = Position.NONE;
0371: pos = DomPosition.NONE;
0372: } else {
0373: pos = getPosition(prevX, prevY);
0374:
0375: // if ((pos == Position.NONE) && webform.getDocument().isGridMode()) {
0376: // if ((pos == Position.NONE) && webform.isGridModeDocument()) {
0377: // if ((pos == DomPosition.NONE) && webform.isGridModeDocument()) {
0378: if ((pos == DomPosition.NONE)
0379: && webform.isGridMode()) {
0380: // Safety net: in page grid mode, if we
0381: // can't find a valid position, position
0382: // it at the absolute position instead
0383: grid = true;
0384: }
0385: }
0386:
0387: // if ((pos != Position.NONE) || (grid && hasMoved(prevX, prevY))) {
0388: if ((pos != DomPosition.NONE)
0389: || (grid && hasMoved(prevX, prevY))) {
0390: // gm.move(pane, /*beans,*/ selections, boxes, pos, prevX, prevY,
0391: // action == DRAG_FREE);
0392: List<Point> points = new ArrayList<Point>();
0393: // XXX #118153 Possible NPE.
0394: if (selections != null) {
0395: for (Rectangle selection : selections) {
0396: points.add(new Point(selection.x,
0397: selection.y));
0398: }
0399: // gm.move(pane, /*beans,*/ points.toArray(new Point[points.size()]), boxes.toArray(new CssBox[boxes.size()]),
0400: // pos, prevX, prevY, action == DRAG_FREE);
0401: webform.getDomDocument().moveComponents(
0402: webform,
0403: boxes.toArray(new CssBox[boxes
0404: .size()]),
0405: points.toArray(new Point[points
0406: .size()]), pos, prevX,
0407: prevY, !(action == DRAG_FREE));
0408: }
0409: } // else: didn't really move ...
0410: }
0411:
0412: e.consume();
0413: }
0414: } finally {
0415: selections = null;
0416: previousCursor = null;
0417: images = null;
0418: }
0419: }
0420:
0421: /**
0422: * Moves the dragging rectangles
0423: */
0424: public void mouseDragged(MouseEvent e) {
0425: Point p = e.getPoint();
0426: prevMouseX = p.x;
0427: prevMouseY = p.y;
0428: update(e, p.x, p.y);
0429: webform.getPane().scrollRectToVisible(new Rectangle(p));
0430: }
0431:
0432: /** Return the positioning parent associated with a given dragged box.
0433: * This is the parent we should compute relative indices to.
0434: */
0435: private CssBox getPositionedBy(int index) {
0436: CssBox box = (CssBox) boxes.get(index);
0437:
0438: return box.getPositionedBy();
0439: }
0440:
0441: /** Draw the selection rectangles at the given position
0442: */
0443: public void paint(Graphics g) {
0444: Graphics2D g2d = (Graphics2D) g;
0445: int x = prevX;
0446: int y = prevY;
0447:
0448: if (action == LINKING) {
0449: if (webform.getManager().isHighlighted()) {
0450: g.setColor(Color.BLUE);
0451: } else {
0452: g.setColor(Color.BLACK);
0453: }
0454:
0455: if (linkStroke == null) {
0456: int width = 1;
0457: linkStroke = new BasicStroke((float) width,
0458: BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER,
0459: 10.0f, new float[] { 4 * width,
0460: (4 * width) + width }, 0.0f);
0461: }
0462:
0463: Stroke oldStroke = g2d.getStroke();
0464: g2d.setStroke(linkStroke);
0465: g.drawLine(startX, startY, x, y);
0466: g2d.setStroke(oldStroke);
0467:
0468: // TODO Arrowhead? If you do, don't forget to update code
0469: // which produces the dirty rectangle either
0470: return;
0471: }
0472:
0473: if (!hasMoved(x, y)) {
0474: return;
0475: }
0476:
0477: // if (pos != Position.NONE) {
0478: if (pos != DomPosition.NONE) {
0479: // We're over a text flow area - simply show the caret
0480: return;
0481: }
0482:
0483: // Draw a dashed line instead of a solid line?
0484: // float[] dash = {1.0f, 7.0f}; // make a 1-pixel dot every 8th pixel
0485: // g.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_SQUARE,
0486: // BasicStroke.JOIN_MITER,
0487: // 10.0f, dash, 0.0f));
0488: if (selections == null) {
0489: return;
0490: }
0491:
0492: int n = selections.size();
0493: // GridHandler gm = GridHandler.getInstance();
0494: // GridHandler gm = webform.getGridHandler();
0495: Composite oldComposite = null;
0496:
0497: if (DISPLAY_IMAGES) {
0498: oldComposite = g2d.getComposite();
0499:
0500: if (alpha == null) {
0501: alpha = AlphaComposite.getInstance(
0502: AlphaComposite.SRC_OVER, 0.7f);
0503: }
0504:
0505: g2d.setComposite(alpha);
0506: }
0507:
0508: for (int i = 0; i < n; i++) {
0509: Rectangle r = (Rectangle) selections.get(i);
0510:
0511: int xp;
0512: int yp;
0513:
0514: if (action == DRAG_FREE) {
0515: xp = r.x + x;
0516: yp = r.y + y;
0517: } else if (hasMoved(x, y)) {
0518: // GridHandler gm = GridHandler.getDefault();
0519: // xp = gm.snapX(r.x + x, getPositionedBy(i));
0520: // yp = gm.snapY(r.y + y, getPositionedBy(i));
0521: xp = webform.snapX(r.x + x, getPositionedBy(i));
0522: yp = webform.snapY(r.y + y, getPositionedBy(i));
0523: } else {
0524: xp = r.x + startX;
0525: yp = r.y + startY;
0526: }
0527:
0528: List<Image> images = null;
0529:
0530: if (DISPLAY_IMAGES) {
0531: images = getImages();
0532: }
0533:
0534: if ((images != null) && (images.get(i) != null)) {
0535: Image image = images.get(i);
0536:
0537: if (image != null) {
0538: transform.setToTranslation((float) xp, (float) yp);
0539: g2d.drawImage(image, transform, null);
0540: }
0541: } else {
0542: if (DesignerPane.useAlpha) {
0543: g.setColor(webform.getColors().draggerColor);
0544: g.fillRect(xp + 1, yp + 1, r.width - 1,
0545: r.height - 1);
0546: g.setColor(webform.getColors().draggerColorBorder);
0547: } else {
0548: g.setColor(Color.BLACK);
0549: }
0550:
0551: g.drawRect(xp, yp, r.width, r.height);
0552: }
0553: }
0554:
0555: if (oldComposite != null) {
0556: g2d.setComposite(oldComposite);
0557: }
0558: }
0559:
0560: // "Too much"; initiating a drag is a bit different than
0561: // adjusting the position of a component.
0562: //private static final int THRESHOLD = BasicDragGestureRecognizer.getMotionThreshold();
0563:
0564: /** Return true if the mouse pointer has moved (significantly)
0565: * since dragging was initiated. If not, we shouldn't snap to
0566: * grid for example - otherwise, simply clicking and releasing
0567: * on a component will move it to a grid location - not what we want.
0568: * If snap-to-grid is off, returns true if the coordinate has
0569: * moved any pixels since dragging began.
0570: * Once the mouse has moved from the original starting position,
0571: * this method will return true even over the original point.
0572: *
0573: * @param x The x coordinate of the reference point
0574: * @param y The y coordinate of the reference point
0575: * @return true iff we should snap the rectangle for the given reference
0576: * (user mouse pointer) position.
0577: */
0578: private boolean hasMoved(int x, int y) {
0579: if (alreadyMoved) {
0580: return true;
0581: }
0582:
0583: // if (!GridHandler.getInstance().snap() || (action == DRAG_FREE)) {
0584: // if (!webform.getGridHandler().snap() || (action == DRAG_FREE)) {
0585: // if (!GridHandler.getDefault().isSnap() || (action == DRAG_FREE)) {
0586: if (!webform.isGridSnap() || (action == DRAG_FREE)) {
0587: return (x != startX) || (y != startY);
0588: }
0589:
0590: int distX = x - startX;
0591:
0592: if (distX < 0) {
0593: distX = -distX;
0594: }
0595:
0596: int distY = y - startY;
0597:
0598: if (distY < 0) {
0599: distY = -distY;
0600: }
0601:
0602: alreadyMoved = (distX > THRESHOLD) || (distY > THRESHOLD);
0603:
0604: return alreadyMoved;
0605: }
0606:
0607: /**
0608: * Start the dragger by setting the dragging cursor and
0609: * drawing dragging rectangles.
0610: */
0611: public void mousePressed(MouseEvent e) {
0612: if (!e.isConsumed()) {
0613: updateSnapState(e);
0614:
0615: Point p = e.getPoint();
0616: DesignerPane pane = webform.getPane();
0617: pane.addKeyListener(this );
0618: previousCursor = pane.getCursor();
0619: pane.setCursor(Cursor
0620: .getPredefinedCursor(Cursor.MOVE_CURSOR));
0621:
0622: startX = p.x;
0623: startY = p.y;
0624: prevX = p.x;
0625: prevY = p.y;
0626: prevAction = getDragAction(e);
0627: prevMouseX = p.x;
0628: prevMouseY = p.y;
0629:
0630: ImageIcon imgIcon = // XXX Does this have any effect anymore?
0631: new ImageIcon(
0632: Dragger.class
0633: .getResource("/org/netbeans/modules/visualweb/designer/resources/drag_position.gif"));
0634: // StatusDisplayer_RAVE.getRaveDefault().setPositionLabelIcon(imgIcon);
0635:
0636: e.consume();
0637: }
0638: }
0639:
0640: /**
0641: * Report whether or not the components have been dragged from their
0642: * original positions.
0643: */
0644: public boolean hasMoved() {
0645: return hasMoved(prevX, prevY);
0646: }
0647:
0648: private List<Image> getImages() {
0649: if (images == null) {
0650: initializeImages();
0651: }
0652:
0653: return images;
0654: }
0655:
0656: private void initializeImages() {
0657: transform = new AffineTransform();
0658:
0659: int n = selections.size();
0660: images = new ArrayList<Image>(n);
0661:
0662: for (int i = 0; i < n; i++) {
0663: Image image = null;
0664: CssBox box = (CssBox) boxes.get(i);
0665: Rectangle r = (Rectangle) selections.get(i);
0666:
0667: if ((r.width > 0) && (r.height > 0)
0668: && ((float) r.width * r.height < Integer.MAX_VALUE)) { // XXX #6466015 Possible IAE from java.awt.image.SampleModel
0669: image = new BufferedImage(r.width, r.height,
0670: BufferedImage.TYPE_INT_RGB);
0671:
0672: if (image != null) {
0673: Graphics og = image.getGraphics(); // offscreen buffer
0674:
0675: try {
0676: og.setClip(0, 0, r.width, r.height);
0677:
0678: //og.setColor(new Color(0, 255, 0, 0));
0679: // Some components are transparent, so we have to
0680: // paint the background. XXX I should ask them so
0681: // I don't have to waste time here!!!
0682: PageBox pageBox = box.getWebForm().getPane()
0683: .getPageBox();
0684: og.setColor(pageBox.getBackground());
0685: og.fillRect(0, 0, r.width, r.height);
0686: DesignerPane.clip.setBounds(0, 0, r.width,
0687: r.height);
0688: DesignerPane.clipBr.x = r.width;
0689: DesignerPane.clipBr.y = r.height;
0690:
0691: // Turn off clip optimizations since they depend on box extents
0692: // which we're not recomputing for this box
0693: boolean oldClip = DesignerPane.INCREMENTAL_LAYOUT;
0694: DesignerPane.INCREMENTAL_LAYOUT = false;
0695:
0696: if (box.getX() == CssBox.UNINITIALIZED) {
0697: // Some components paint into line boxes. These may be
0698: // broken up into non-rectangular boxes. (Imagine a StaticText
0699: // consisting of two words, where the first word fits at the
0700: // end of a line, and the second word fits at the beginning of
0701: // the next). In this case we cannot simply paint the box,
0702: // we have to paint its "context", e.g. the linebox. We've
0703: // already computed the bounding box in the dragger code.
0704: // This is set as the clip region. Paint the full page with
0705: // a clip such that the component itself ends up at (0,0).
0706: try {
0707: pageBox.paint(og, -(startX + r.x),
0708: -(startY + r.y));
0709: } finally {
0710: DesignerPane.INCREMENTAL_LAYOUT = oldClip;
0711: }
0712: } else {
0713: int oldX = box.getX();
0714: int oldY = box.getY();
0715: int oldLeftMargin = box.getLeftMargin();
0716: int oldEffectiveTopMargin = box
0717: .getEffectiveTopMargin();
0718:
0719: box.setLocation(0, 0);
0720: box.setMargins(0, 0);
0721:
0722: try {
0723: box.paint(og, 0, 0);
0724: } finally {
0725: DesignerPane.INCREMENTAL_LAYOUT = oldClip;
0726: box.setLocation(oldX, oldY);
0727: box.setMargins(oldLeftMargin,
0728: oldEffectiveTopMargin);
0729: }
0730: }
0731: } finally {
0732: og.dispose();
0733: }
0734: }
0735: }
0736:
0737: images.add(image); // even if image is null want it in the list
0738: }
0739: }
0740:
0741: // Release snap to grid with shift key
0742: private void updateSnapState(InputEvent e) {
0743: int oldAction = action;
0744: action = getDragAction(e);
0745:
0746: if ((oldAction == LINKING) && (action != LINKING)) {
0747: webform.getPane().getDndHandler().clearDropMatch();
0748: }
0749: }
0750:
0751: /** Compute a potential drop position under the pointer, if one
0752: * is allowed. Otherwise returns Position.NONE.
0753: */
0754: // private Position getPosition(int px, int py) {
0755: private DomPosition getPosition(int px, int py) {
0756: // Update caret if over a text area. But not if it's over
0757: // one of the being-dragged areas!
0758: // CssBox box = webform.getMapper().findBox(px, py);
0759: CssBox box = ModelViewMapper.findBox(webform.getPane()
0760: .getPageBox(), px, py);
0761:
0762: if (box.getWebForm() != webform) {
0763: // XXX #6366584 The box is from different DOM, e.g. it is over fragment.
0764: // return Position.NONE;
0765: return DomPosition.NONE;
0766: }
0767:
0768: if (isBelowDragged(box)) {
0769: // return Position.NONE;
0770: return DomPosition.NONE;
0771:
0772: /*
0773: } else if (!(box instanceof TextBox) && box.isReplacedBox()) {
0774: System.out.println("Over non-text replaced box");
0775: return Position.NONE;
0776: */
0777: } else {
0778: if (box.isGrid()) {
0779: // return Position.NONE;
0780: return DomPosition.NONE;
0781: }
0782:
0783: // Position pos = webform.getManager().findTextPosition(px, py);
0784: DomPosition pos = webform.getManager().findTextPosition(px,
0785: py);
0786:
0787: // if ((pos == Position.NONE) || canDropAt(pos)) {
0788: if ((pos == DomPosition.NONE) || canDropAt(pos)) {
0789: return pos;
0790: }
0791:
0792: // return Position.NONE;
0793: return DomPosition.NONE;
0794: }
0795: }
0796:
0797: // private boolean canDropAt(Position pos) {
0798: private boolean canDropAt(DomPosition pos) {
0799: // Look up the nearest parent bean at the position
0800: // assert pos != Position.NONE;
0801: if (pos == DomPosition.NONE) {
0802: ErrorManager.getDefault().notify(
0803: ErrorManager.INFORMATIONAL,
0804: new IllegalArgumentException(
0805: "Invalid position, pos=" + pos)); // NOI18N
0806: return false;
0807: }
0808: //
0809: // DesignBean parent = null;
0810: Node curr = pos.getNode();
0811: //
0812: // while (curr != null) {
0813: //// if (curr instanceof RaveElement) {
0814: //// parent = ((RaveElement)curr).getDesignBean();
0815: // if (curr instanceof Element) {
0816: //// parent = InSyncService.getProvider().getMarkupDesignBeanForElement((Element)curr);
0817: // parent = WebForm.getDomProviderService().getMarkupDesignBeanForElement((Element)curr);
0818: //
0819: // if (parent != null) {
0820: // break;
0821: // }
0822: // }
0823: //
0824: // curr = curr.getParentNode();
0825: // }
0826: //
0827: // if (parent == null) {
0828: // return true;
0829: // }
0830: //
0831: // // See if ALL the beans being dragged can be dropped here
0832: // LiveUnit unit = webform.getModel().getLiveUnit();
0833: //
0834: // for (int i = 0, n = beans.size(); i < n; i++) {
0835: // DesignBean bean = (DesignBean)beans.get(i);
0836: // String className = bean.getInstance().getClass().getName();
0837: //
0838: // if (!unit.canCreateBean(className, parent, null)) {
0839: // return false;
0840: // }
0841: //
0842: // // Ensure that we're not trying to drop a html bean on a
0843: // // renders-children parent
0844: // boolean isHtmlBean = className.startsWith(HtmlBean.PACKAGE);
0845: //
0846: // if (isHtmlBean) {
0847: // // We can't drop anywhere below a "renders children" JSF
0848: // // component
0849: //// if (parent != FacesSupport.findHtmlContainer(webform, parent)) {
0850: // if (parent != webform.findHtmlContainer(parent)) {
0851: // return false;
0852: // }
0853: // }
0854: // }
0855: //
0856: // return true;
0857:
0858: // return webform.canDropDesignBeansAtNode(
0859: // beans == null
0860: // ? new DesignBean[0]
0861: // : (DesignBean[])beans.toArray(new DesignBean[beans.size()]),
0862: // curr);
0863: return webform.canDropComponentsAtNode(componentRootElements
0864: .clone(), curr);
0865: }
0866:
0867: /** Determine whether the given box is inside one of the boxes we're dragging */
0868: private boolean isBelowDragged(CssBox box) {
0869: for (int i = 0, n = boxes.size(); i < n; i++) {
0870: CssBox curr = box;
0871: CssBox draggedBox = (CssBox) boxes.get(i);
0872:
0873: while (curr != null) {
0874: if (curr == draggedBox) {
0875: return true;
0876: }
0877:
0878: curr = curr.getParent();
0879: }
0880: }
0881:
0882: return false;
0883: }
0884:
0885: /** Determine whether the given x,y position is over a grid area.
0886: * If the x,y is over one of the objects being dragged, we look "under" it.
0887: */
0888: private boolean isOverGrid(int px, int py) {
0889: // CssBox box = webform.getMapper().findBox(px, py);
0890: CssBox box = ModelViewMapper.findBox(webform.getPane()
0891: .getPageBox(), px, py);
0892:
0893: CssBox curr = box;
0894:
0895: while (curr != null) {
0896: // MarkupDesignBean currMarkupDesignBean = CssBox.getMarkupDesignBeanForCssBox(curr);
0897: Element currComponentRootElement = CssBox
0898: .getElementForComponentRootCssBox(curr);
0899: // if (curr.getDesignBean() != null) {
0900: // if (currMarkupDesignBean != null) {
0901: if (currComponentRootElement != null) {
0902: // If we're within a component being dragged, look "under it".
0903: // No - that's no good -- there might be another absolutely positioned
0904: // component at this position that is NOT the parent; we should use
0905: // it instead. I guess findBox should take an ignore-list?
0906: // if (isDragged(curr.getDesignBean())) {
0907: // if (isDragged(currMarkupDesignBean)) {
0908: if (isDraggedComponent(currComponentRootElement)) {
0909: box = curr.getParent();
0910: } else {
0911: // If we're within a component that is not a container, look "under it".
0912: // BeanInfo bi = curr.getDesignBean().getBeanInfo();
0913: // BeanInfo bi = currMarkupDesignBean.getBeanInfo();
0914: //
0915: // if (bi != null) {
0916: // BeanDescriptor bd = bi.getBeanDescriptor();
0917: // Object o = bd.getValue(Constants.BeanDescriptor.IS_CONTAINER);
0918: // boolean notContainer = o == Boolean.FALSE;
0919: //
0920: // if (notContainer) {
0921: // box = curr.getParent();
0922: // }
0923: // }
0924: if (!webform.getDomProviderService()
0925: .isContainerComponent(
0926: currComponentRootElement)) {
0927: box = curr.getParent();
0928: }
0929: }
0930: }
0931:
0932: curr = curr.getParent();
0933: }
0934:
0935: return (box == null) || box.isGrid();
0936: }
0937:
0938: /** Return true iff the given DesignBean is being dragged, or if one of its
0939: * ancestors are being dragged.
0940: */
0941: // private boolean isDragged(DesignBean bean) {
0942: private boolean isDraggedComponent(Element componentRootElement) {
0943: // for (int i = 0, n = beans.size(); i < n; i++) {
0944: //// DesignBean lb = (DesignBean)beans.get(i);
0945: // MarkupDesignBean lb = beans.get(i);
0946: for (Element lbElement : componentRootElements) {
0947:
0948: // if (bean == lb) {
0949: // return true;
0950: // }
0951: if (lbElement == componentRootElement) {
0952: return true;
0953: }
0954: }
0955:
0956: // if (bean.getBeanParent() == null) {
0957: // return false;
0958: // } else {
0959: // return isDragged(bean.getBeanParent());
0960: // }
0961: Element parentComponentRootElement = webform
0962: .getDomProviderService().getParentComponent(
0963: componentRootElement);
0964: return parentComponentRootElement == null ? false
0965: : isDraggedComponent(parentComponentRootElement);
0966: }
0967:
0968: private void update(InputEvent e, int px, int py) {
0969: if (!e.isConsumed()) {
0970: int oldAction = action;
0971: updateSnapState(e);
0972:
0973: DesignerPane pane = webform.getPane();
0974:
0975: // GridHandler gm = GridHandler.getInstance();
0976: // GridHandler gm = webform.getGridHandler();
0977: // GridHandler gm = GridHandler.getDefault();
0978: // DesignerPane.clearDirty();
0979: pane.clearDirty();
0980:
0981: // XXX #94643 Avoiding possible NPE.
0982: if (selections == null) {
0983: return;
0984: }
0985:
0986: if (!DesignerPane.INCREMENTAL_LAYOUT) {
0987: // DesignerPane.addDirtyPoint(0, 0);
0988: pane.addDirtyPoint(0, 0);
0989: }
0990:
0991: if (prevAction == LINKING) {
0992: // DesignerPane.addDirtyPoint(startX-1, startY-1);
0993: // DesignerPane.addDirtyPoint(startX+1, startY+1);
0994: // DesignerPane.addDirtyPoint(prevX-1, prevY-1);
0995: // DesignerPane.addDirtyPoint(prevX+1, prevY+1);
0996: pane.addDirtyPoint(startX - 1, startY - 1);
0997: pane.addDirtyPoint(startX + 1, startY + 1);
0998: pane.addDirtyPoint(prevX - 1, prevY - 1);
0999: pane.addDirtyPoint(prevX + 1, prevY + 1);
1000: } else if (prevX != -500) {
1001: int n = selections.size();
1002:
1003: for (int i = 0; i < n; i++) {
1004: Rectangle r2 = (Rectangle) selections.get(i);
1005: int x;
1006: int y;
1007:
1008: if (oldAction == DRAG_FREE) {
1009: x = r2.x + prevX;
1010: y = r2.y + prevY;
1011: } else if (hasMoved(prevX, prevY)) {
1012: // x = gm.snapX(r2.x + prevX, getPositionedBy(i));
1013: // y = gm.snapY(r2.y + prevY, getPositionedBy(i));
1014: x = webform.snapX(r2.x + prevX,
1015: getPositionedBy(i));
1016: y = webform.snapY(r2.y + prevY,
1017: getPositionedBy(i));
1018: } else {
1019: x = r2.x + startX;
1020: y = r2.y + startY;
1021: }
1022:
1023: // DesignerPane.addDirtyPoint(x, y);
1024: // DesignerPane.addDirtyPoint(x + r2.width + 1, y + r2.height + 1);
1025: pane.addDirtyPoint(x, y);
1026: pane.addDirtyPoint(x + r2.width + 1, y + r2.height
1027: + 1);
1028: }
1029: }
1030:
1031: prevX = px;
1032: prevY = py;
1033: prevAction = action;
1034:
1035: int n = selections.size();
1036:
1037: if (action == LINKING) {
1038: // DesignerPane.addDirtyPoint(startX-1, startY-1);
1039: // DesignerPane.addDirtyPoint(startX+1, startY+1);
1040: // DesignerPane.addDirtyPoint(px-1, py-1);
1041: // DesignerPane.addDirtyPoint(px+1, py+1);
1042: pane.addDirtyPoint(startX - 1, startY - 1);
1043: pane.addDirtyPoint(startX + 1, startY + 1);
1044: pane.addDirtyPoint(px - 1, py - 1);
1045: pane.addDirtyPoint(px + 1, py + 1);
1046: } else {
1047: for (int i = 0; i < n; i++) {
1048: Rectangle r2 = (Rectangle) selections.get(i);
1049: int x;
1050: int y;
1051:
1052: if (action == DRAG_FREE) {
1053: x = r2.x + prevX;
1054: y = r2.y + prevY;
1055: } else if (hasMoved(prevX, prevY)) {
1056: // x = gm.snapX(r2.x + prevX, getPositionedBy(i));
1057: // y = gm.snapY(r2.y + prevY, getPositionedBy(i));
1058: x = webform.snapX(r2.x + prevX,
1059: getPositionedBy(i));
1060: y = webform.snapY(r2.y + prevY,
1061: getPositionedBy(i));
1062: } else {
1063: x = r2.x + startX;
1064: y = r2.y + startY;
1065: }
1066:
1067: // DesignerPane.addDirtyPoint(x, y);
1068: // DesignerPane.addDirtyPoint(x + r2.width + 1, y + r2.height + 1);
1069: pane.addDirtyPoint(x, y);
1070: pane.addDirtyPoint(x + r2.width + 1, y + r2.height
1071: + 1);
1072: }
1073: }
1074:
1075: // Update caret if over a text area. But not if it's over
1076: // one of the being-dragged areas!
1077: boolean grid = isOverGrid(px, py);
1078:
1079: if (grid) {
1080: // pos = Position.NONE;
1081: pos = DomPosition.NONE;
1082: } else {
1083: pos = getPosition(px, py);
1084:
1085: // if ((pos == Position.NONE) && webform.getDocument().isGridMode()) {
1086: // if ((pos == Position.NONE) && webform.isGridModeDocument()) {
1087: // if ((pos == DomPosition.NONE) && webform.isGridModeDocument()) {
1088: if ((pos == DomPosition.NONE) && webform.isGridMode()) {
1089: // Safety net: in page grid mode, if we can't find a valid
1090: // position, position it at the absolute position instead
1091: grid = true;
1092: }
1093: }
1094:
1095: if (action == LINKING) {
1096: webform.getPane().hideCaret();
1097:
1098: // DesignBean candidate = getLinkParticipant(startX, startY);
1099: // Element candidate = getLinkParticipant(startX, startY);
1100: Element candidate = ModelViewMapper.findElement(webform
1101: .getPane().getPageBox(), startX, startY);
1102:
1103: if ((candidate == null) ||
1104: // (webform.getPane().getDndHandler().getDropTypeForClassNamesEx(new Point(px, py),
1105: // new String[] { candidate.getInstance().getClass().getName() },
1106: // new DesignBean[] { candidate }, true) != DndHandler.DROP_LINKED)) {
1107: (webform.getPane().getDndHandler()
1108: .getDropTypeForComponent(
1109: new Point(px, py), candidate,
1110: true) != DndHandler.DROP_LINKED)) {
1111: // Try reverse link
1112: // candidate = getLinkParticipant(px, py);
1113: candidate = ModelViewMapper.findElement(webform
1114: .getPane().getPageBox(), px, py);
1115:
1116: if ((candidate == null) ||
1117: // (webform.getPane().getDndHandler().getDropTypeForClassNamesEx(new Point(startX, startY),
1118: // new String[] { candidate.getInstance().getClass().getName() },
1119: // new DesignBean[] { candidate }, true) != DndHandler.DROP_LINKED)) {
1120: (webform.getPane().getDndHandler()
1121: .getDropTypeForComponent(
1122: new Point(startX, startY),
1123: candidate, true) != DndHandler.DROP_LINKED)) {
1124: webform.getPane().getDndHandler()
1125: .clearDropMatch();
1126: pane
1127: .setCursor(Cursor
1128: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1129: } else {
1130: pane.setCursor(webform.getManager()
1131: .getLinkedCursor());
1132: }
1133: } else {
1134: pane.setCursor(webform.getManager()
1135: .getLinkedCursor());
1136: }
1137: } else if (!grid) {
1138: // Position pos = ModelViewMapper.viewToModel(webform, px, py);
1139: DomPosition pos = ModelViewMapper.viewToModel(webform,
1140: px, py);
1141:
1142: // if (pos != Position.NONE) {
1143: if (pos != DomPosition.NONE) {
1144: webform.getPane().showCaret(pos);
1145: pane.setCursor(Cursor
1146: .getPredefinedCursor(Cursor.TEXT_CURSOR));
1147: } else {
1148: webform.getPane().hideCaret();
1149: pane.setCursor(Cursor
1150: .getPredefinedCursor(Cursor.MOVE_CURSOR));
1151: }
1152: } else {
1153: webform.getPane().hideCaret();
1154: pane.setCursor(Cursor
1155: .getPredefinedCursor(Cursor.MOVE_CURSOR));
1156: }
1157:
1158: pane.repaintDirty(false);
1159:
1160: e.consume();
1161:
1162: // Show position of component in the status line
1163: if ((n > 0) && (action != LINKING)) {
1164: int i = 0;
1165: Rectangle r = (Rectangle) selections.get(i);
1166: int x;
1167: int y;
1168:
1169: if (action == DRAG_FREE) {
1170: x = r.x + prevX;
1171: y = r.y + prevY;
1172: } else if (hasMoved(prevX, prevY)) {
1173: // x = gm.snapX(r.x + prevX, getPositionedBy(i));
1174: // y = gm.snapY(r.y + prevY, getPositionedBy(i));
1175: x = webform.snapX(r.x + prevX, getPositionedBy(i));
1176: y = webform.snapY(r.y + prevY, getPositionedBy(i));
1177: } else {
1178: x = r.x + startX;
1179: y = r.y + startY;
1180: }
1181:
1182: // StatusDisplayer_RAVE.getRaveDefault().setPositionLabelText(x + "," + y);
1183: }
1184: }
1185: }
1186:
1187: // --- implements KeyListener ---
1188: public void keyPressed(KeyEvent e) {
1189: if (action != getDragAction(e)) {
1190: update(e, prevMouseX, prevMouseY);
1191: }
1192: }
1193:
1194: public void keyReleased(KeyEvent e) {
1195: if (action != getDragAction(e)) {
1196: update(e, prevMouseX, prevMouseY);
1197: }
1198: }
1199:
1200: public void keyTyped(KeyEvent e) {
1201: }
1202:
1203: private int getDragAction(InputEvent e) {
1204: if (e.isShiftDown()) {
1205: if (e.isControlDown()) {
1206: return LINKING;
1207: } else {
1208: return DRAG_FREE;
1209: }
1210: } else {
1211: return DRAG_GRID;
1212: }
1213: }
1214:
1215: //// private DesignBean getLinkParticipant(int x, int y) {
1216: // private Element getLinkParticipant(int x, int y) {
1217: //// Object droppedUpon = webform.getMapper().findComponent(x, y);
1218: ////
1219: //// if (droppedUpon instanceof DesignBean) {
1220: //// return (DesignBean)droppedUpon;
1221: //// }
1222: ////
1223: //// return null;
1224: //// return ModelViewMapper.findComponent(webform.getPane().getPageBox(), x, y);
1225: //// return ModelViewMapper.findMarkupDesignBean(webform.getPane().getPageBox(), x, y);
1226: // return ModelViewMapper.findElement(webform.getPane().getPageBox(), x, y);
1227: // }
1228: }
|