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.css2;
0042:
0043: import org.netbeans.modules.visualweb.designer.CssUtilities;
0044: import java.awt.Color;
0045: import java.awt.Dimension;
0046: import java.awt.Graphics;
0047: import java.awt.Graphics2D;
0048: import java.awt.GraphicsConfiguration;
0049: import java.awt.Image;
0050: import java.awt.Insets;
0051: import java.awt.Point;
0052: import java.awt.Rectangle;
0053: import java.awt.RenderingHints;
0054: import java.awt.geom.AffineTransform;
0055: import java.awt.image.BufferedImage;
0056:
0057: import javax.swing.JScrollPane;
0058: import javax.swing.JViewport;
0059: import javax.swing.event.ChangeEvent;
0060: import javax.swing.event.ChangeListener;
0061:
0062: import org.openide.ErrorManager;
0063: import org.w3c.dom.DocumentFragment;
0064: import org.w3c.dom.Element;
0065: import org.w3c.dom.Node;
0066: import org.w3c.dom.NodeList;
0067:
0068: import org.netbeans.modules.visualweb.api.designer.DomProvider.DomPosition;
0069: import org.netbeans.modules.visualweb.designer.ColorManager;
0070: import org.netbeans.modules.visualweb.designer.DesignerPane;
0071: import org.netbeans.modules.visualweb.designer.DesignerUtils;
0072: import org.netbeans.modules.visualweb.designer.WebForm;
0073: import org.netbeans.modules.visualweb.api.designer.cssengine.XhtmlCss;
0074: import org.netbeans.modules.visualweb.designer.html.HtmlTag;
0075:
0076: /**
0077: * Represents the page/document <body> tag - the initial containing block.
0078: * <b>Note: you must</b> call setViewport on
0079: * this component after it has been added to a JScrollPane; otherwise,
0080: * it will not relayout after a resize.
0081: * @todo This box is also used (as an ancestor class) for frames/iframes
0082: * now. Rename it to DocumentBox - and put the page specific stuff into
0083: * a subclass which doesn't share with FrameBox?
0084: *
0085: * @author Tor Norbye.
0086: */
0087:
0088: public class PageBox extends DocumentBox implements ChangeListener {
0089: private boolean dark;
0090: private Color constraintsColor = null;
0091: private Color constraintsColorLight = null;
0092:
0093: /** True if this pagebox represents a top level box like a <body> or the
0094: * outermost <frameset> tag */
0095: protected boolean isTopLevel = true;
0096: private int componentVisibleWidth = Integer.MAX_VALUE;
0097: private int componentVisibleHeight = Integer.MAX_VALUE;
0098: private CssBox selected;
0099:
0100: /** Creates a new instance of PageBox suitable for a root level
0101: * document. Use Factory method instead.
0102: */
0103: private PageBox(DesignerPane pane, WebForm webform, Element body) {
0104: this (pane, webform, body, BoxType.STATIC, false, false);
0105: }
0106:
0107: /** Creates a new instance of PageBox */
0108: protected PageBox(DesignerPane pane, WebForm webform, Element body,
0109: BoxType boxType, boolean inline, boolean replaced) {
0110: // XXX What do we pass in as a containing block?
0111: super (pane, webform, body, boxType, inline, replaced);
0112: this .pane = pane;
0113: this .body = body;
0114:
0115: if (bg == null) {
0116: // Default to gray instead of white so that stylesheet switch
0117: // to white is a visual change
0118: //bg = new Color(240, 240, 240); // #f0f0f0
0119: //bg = new Color(245, 245, 245); // SVG "whitesmoke", #f5f5f5
0120: bg = Color.WHITE;
0121: }
0122:
0123: x = 0;
0124: y = 0;
0125: width = 0;
0126: height = 0;
0127: }
0128:
0129: /** Creates a new instance of PageBox suitable for a root level
0130: * document.
0131: */
0132: public static PageBox getPageBox(DesignerPane pane,
0133: WebForm webform, Element element) {
0134: if (element.getTagName().equals(HtmlTag.FRAMESET.name)) {
0135: return FrameSetBox.getFrameSetBox(pane, webform, element,
0136: BoxType.STATIC, HtmlTag.FRAMESET, false);
0137: }
0138:
0139: return new PageBox(pane, webform, element);
0140: }
0141:
0142: /** Called when the page box is added to a box hierarchy */
0143: public void boxAdded() {
0144: }
0145:
0146: /** Called when the page box is removed/getting unused */
0147: public void boxRemoved() {
0148: if (viewport != null) {
0149: viewport.removeChangeListener(this );
0150: }
0151: }
0152:
0153: /** Return the page background color */
0154: public Color getBackground() {
0155: return bg;
0156: }
0157:
0158: public void paint(Graphics g) {
0159: paint(g, 0, 0); // NOT getAbsoluteX()/getAbsoluteY(), since paint will call super which adds them in
0160: }
0161:
0162: // Get rid of selected box after a remove
0163: public void removed(Node node, Node parent) {
0164: super .removed(node, parent);
0165: selected = null;
0166: }
0167:
0168: public void paint(Graphics g, int px, int py) {
0169: if (!isTopLevel) {
0170: super .paint(g, px, py);
0171:
0172: return;
0173: }
0174:
0175: //if (debugpaint) {
0176: // Log.err.log("PageBox.paint - layotValid=" + layoutValid);
0177: // Log.err.log("background=" + bg);
0178: //}
0179: layout();
0180:
0181: if (!layoutValid) {
0182: return;
0183: }
0184:
0185: super .paint(g, px, py);
0186:
0187: // FOR DEBUGGING PURPOSES ONLY
0188: if (org.netbeans.modules.visualweb.designer.InteractionManager.ENABLE_DOM_INSPECTOR
0189: && (selected != null) && (selected != this )) {
0190: int bx = selected.getAbsoluteX();
0191: int by = selected.getAbsoluteY();
0192: int bw = selected.getWidth();
0193: int bh = selected.getHeight();
0194:
0195: if ((Math.abs(bx) < 50000) && (Math.abs(by) < 50000)
0196: && (Math.abs(bw) < 50000) && (Math.abs(bh) < 50000)) {
0197: g.setColor(Color.RED);
0198: g.drawRect(bx, by, bw, bh);
0199: }
0200: }
0201:
0202: // Constrain
0203: // DesignerSettings designerSettings = DesignerSettings.getInstance();
0204: // if (designerSettings.getPageSizeWidth() != -1) {
0205: // int x2 = designerSettings.getPageSizeWidth();
0206: // int y2 = designerSettings.getPageSizeHeight();
0207: if (webform.getPageSizeWidth() != -1) {
0208: int x2 = webform.getPageSizeWidth();
0209: int y2 = webform.getPageSizeHeight();
0210:
0211: if (constraintsColor == null) {
0212: constraintsColor = new Color(128, 128, 128, 208);
0213: constraintsColorLight = new Color(216, 216, 216, 96);
0214: }
0215:
0216: int w = width;
0217: int h = height;
0218:
0219: g.setColor(constraintsColor);
0220:
0221: if (w > x2) {
0222: g.fillRect(x2, 0, w - x2, h);
0223: }
0224:
0225: g.setColor(constraintsColorLight);
0226:
0227: if (h > y2) {
0228: g.fillRect(0, y2, x2, h - y2);
0229: }
0230:
0231: g.setColor(Color.WHITE);
0232: g.drawString("(" + x2 + "," + y2 + ")", x2 + 10, y2 + 10);
0233: // } else if (webform.isFragment() || webform.isPortlet()) {
0234: } else if (webform.isPaintSizeMask()) {
0235: // See if we should draw page-fragment/portlet size mask
0236: Element e = webform.getHtmlBody();
0237:
0238: if (e != null) {
0239: // int w = CssLookup.getLength(e, XhtmlCss.WIDTH_INDEX);
0240: int w = CssUtilities.getCssLength(e,
0241: XhtmlCss.WIDTH_INDEX);
0242:
0243: if (w != AUTO) {
0244: int h = CssUtilities.getCssLength(e,
0245: XhtmlCss.HEIGHT_INDEX);
0246:
0247: if (h != AUTO) {
0248: int x2 = w;
0249: int y2 = h;
0250:
0251: if (constraintsColor == null) {
0252: constraintsColor = new Color(128, 128, 128,
0253: 208);
0254: constraintsColorLight = new Color(216, 216,
0255: 216, 96);
0256: }
0257:
0258: //g.setColor(constraintsColor);
0259: g.setColor(constraintsColorLight);
0260:
0261: if (width > x2) {
0262: g.fillRect(x2, 0, width - x2, height);
0263: }
0264:
0265: if (height > y2) {
0266: g.fillRect(0, y2, x2, height - y2);
0267: }
0268: }
0269: }
0270: }
0271: }
0272: }
0273:
0274: protected void paintBox(Graphics g, int x, int y, int w, int h) {
0275: webform.getColors().sync();
0276:
0277: if (!isTopLevel || (viewport == null)) {
0278: super .paintBox(g, x, y, w, h);
0279:
0280: return;
0281: }
0282:
0283: int originalX = x;
0284: int originalY = y;
0285: Dimension d = viewport.getExtentSize();
0286: Point p = viewport.getViewPosition();
0287: x = p.x;
0288: y = p.y;
0289:
0290: // int originalWidth = w;
0291: // int originalHeight = h;
0292: w = d.width;
0293: h = d.height;
0294:
0295: // XXX Shouldn't this border be painted like the background relative to the
0296: // box dimensions, not the viewport???
0297: if (border != null) {
0298: // TODO: The border should be painted using the original coordinate
0299: // system too! Otherwise if you set a border on the body, and
0300: // put a wide pre inside it, and move the scrollbar notice how
0301: // the border is moving with the viewport - it should not
0302: //border.paintBorder(g, originalX, originalY, originalWidth, originalHeight);
0303: border.paintBorder(g, x, y, w, h);
0304: }
0305:
0306: // NO! Don't do this if a particular border edge is dashed
0307: // or none - in that case the border should "shine through!
0308: x += leftBorderWidth;
0309: y += topBorderWidth;
0310: w -= leftBorderWidth;
0311: w -= rightBorderWidth;
0312: h -= topBorderWidth;
0313: h -= bottomBorderWidth;
0314:
0315: if (bg != null) {
0316: g.setColor(bg);
0317: g.fillRect(x, y, w, h);
0318: } // XXX should I do an else here??? Am I "double" painting the background?
0319:
0320: if (bgPainter != null) {
0321: // According to the CSS spec section 1.4.21, the background
0322: // image covers the padding rectangle.
0323: // Also, paint at the original position, not the viewport position since
0324: // the image should move when you drag the scrollbars
0325: bgPainter.paint(g, originalX, originalY, w
0326: + (x - originalX), h + (y - originalY));
0327: }
0328:
0329: // if (grid && GridHandler.getInstance().grid()) {
0330: // if (grid && getWebForm().getGridHandler().grid()) {
0331: // if (grid && GridHandler.getDefault().isGrid()) {
0332: if (grid && webform.isGridShow()) {
0333: if (hidden && !(this instanceof PageBox)) { // paint grid for the root pagebox!
0334:
0335: return;
0336: }
0337:
0338: paintGrid(g, x, y, w, h);
0339: }
0340: }
0341:
0342: protected void paintGrid(Graphics g, int x, int y, int w, int h) {
0343: if (!isTopLevel || (viewport == null)) {
0344: return;
0345: }
0346:
0347: Dimension d = viewport.getExtentSize();
0348: Point p = viewport.getViewPosition();
0349:
0350: // We're not painting the box' own boundaries, instead
0351: // we're painting the visible viewport. Because of that,
0352: // we have to make sure the grid is aligned with its
0353: // original origin, not the current scrollbar's origin,
0354: // so snap the viewport origin to the grid size.
0355: int px = p.x;
0356: int py = p.y;
0357: int width = d.width;
0358: int height = d.height;
0359:
0360: if (DesignerPane.INCREMENTAL_LAYOUT) {
0361: // Narrow region if possible
0362: // TODO: "clip" the beginning position too. Perhaps only
0363: // remove to a multiple of the starting position
0364: if (px < DesignerPane.clip.x) {
0365: width -= (DesignerPane.clip.x - px);
0366: px = DesignerPane.clip.x;
0367: }
0368:
0369: if (py < DesignerPane.clip.y) {
0370: height -= (DesignerPane.clip.y - py);
0371: py = DesignerPane.clip.y;
0372: }
0373:
0374: if ((px + width) > DesignerPane.clipBr.x) {
0375: width = DesignerPane.clipBr.x - px;
0376: }
0377:
0378: if ((py + height) > DesignerPane.clipBr.y) {
0379: height = DesignerPane.clipBr.y - py;
0380: }
0381: }
0382:
0383: // GridHandler gh = GridHandler.getInstance();
0384: // GridHandler gh = getWebForm().getGridHandler();
0385: // GridHandler gh = GridHandler.getDefault();
0386: // int gridWidth = gh.getGridWidth();
0387: // int gridHeight = gh.getGridHeight();
0388: int gridWidth = webform.getGridWidth();
0389: int gridHeight = webform.getGridHeight();
0390: int xOffset = (px % gridWidth);
0391: int yOffset = (py % gridHeight);
0392: super .paintGrid(g, px - xOffset, py - yOffset, width + xOffset,
0393: height + yOffset);
0394: }
0395:
0396: public void setViewport(JViewport viewport) {
0397: if (this .viewport != null) {
0398: this .viewport.removeChangeListener(this );
0399: }
0400:
0401: this .viewport = viewport;
0402: }
0403:
0404: public JViewport getViewport() {
0405: return viewport;
0406: }
0407:
0408: public void stateChanged(ChangeEvent e) {
0409: if (e.getSource() == this .viewport) {
0410: // XXX #114381 Don't do anything while pane not shown.
0411: if (!pane.isShowing()) {
0412: return;
0413: }
0414:
0415: boolean scrolled = false;
0416: Point p = viewport.getViewPosition();
0417:
0418: if ((p.x != viewportX) || (p.y != viewportY)) {
0419: viewportX = p.x;
0420: viewportY = p.y;
0421: scrolled = true;
0422: webform.getSelection().notifyScrolled();
0423: }
0424:
0425: if ((fixedBoxes != null) && scrolled) {
0426: updateFixedPositions();
0427: pane.repaint();
0428:
0429: return;
0430: } else if (viewport.getParent() instanceof JScrollPane) {
0431: JScrollPane scrollPane = (JScrollPane) viewport
0432: .getParent();
0433: Dimension extentSize = scrollPane.getSize();
0434:
0435: if (componentVisibleWidth != extentSize.width) {
0436: componentVisibleWidth = extentSize.width;
0437: componentVisibleHeight = extentSize.height;
0438:
0439: if (layoutValid) {
0440: // layoutValid = false;
0441: // pane.repaint();
0442: redoLayout(true);
0443: }
0444:
0445: return;
0446: } else if (componentVisibleHeight != extentSize.height) {
0447: componentVisibleHeight = extentSize.height;
0448:
0449: // we already know componentVisibleWidth is right
0450: if (layoutValid) { // XXX should I force a recompute even if layoutValid? is ok?
0451: // layoutValid = false;
0452: // XXX #110849 Fixing the issue with bad relayout.
0453: // All this construct (layoutValid = false; repaint is very suspiscous
0454: // investigate whether rather relayout shouldn't be used instead.
0455: // currWidth = -1; // force recompute
0456: // pane.repaint();
0457: redoLayout(true);
0458: }
0459:
0460: return;
0461: }
0462: } else {
0463: ErrorManager.getDefault().log(
0464: "Unexpected viewport parent");
0465: }
0466: }
0467: }
0468:
0469: /** Update all the positions of fixed boxes for the new position
0470: * of the viewport.
0471: */
0472: private void updateFixedPositions() {
0473: for (int i = 0, n = fixedBoxes.size(); i < n; i++) {
0474: CssBox box = fixedBoxes.get(i);
0475: box.setLocation(box.left + viewportX, box.top + viewportY);
0476: }
0477: }
0478:
0479: /**
0480: * Layout the page hierarchy.
0481: */
0482: public void layout() {
0483: if (layoutValid) {
0484: return;
0485: }
0486:
0487: //MarkupService.clearErrors(true);
0488: int initialWidth = 0;
0489: int initialHeight = 0;
0490:
0491: // See if we can find the viewport
0492: if (viewport != null) {
0493: // TODO - get rid of the viewport attribute of this bean;
0494: // switch to scrollbar entirely
0495: // I ran into this problem where a table which has a 5px margin
0496: // and a width: 100%, would start the system "oscillating":
0497: // the table width would get computed to 100% of the body width,
0498: // which of course is the viewport size. The margin would get tacked
0499: // on, which would make the width slightly wider than the viewport.
0500: // This would then become the new width - so the scrollbars are
0501: // added by the viewport when we set the new size. This of course
0502: // causes a resize event, and we now read the new viewport width.
0503: // This viewport width is ~20px smaller since the horizontal scrollbar
0504: // has been added. This means we end up computing a total width
0505: // which is less than the width required by the scrollpane, so it
0506: // removes the scrollbar - a new resize event comes in, and (start
0507: // reading this paragraph over again - oscillation.)
0508: // SOOOOOO, instead, we don't want to know the size of the viewport,
0509: // we want to know the full size of the scrollpane - without
0510: // scrollbars.
0511: if (viewport.getParent() instanceof JScrollPane) {
0512: JScrollPane scrollPane = (JScrollPane) viewport
0513: .getParent();
0514: Dimension extentSize = scrollPane.getSize();
0515: initialWidth = extentSize.width;
0516: componentVisibleWidth = extentSize.width;
0517: componentVisibleHeight = extentSize.height;
0518: initialHeight = extentSize.height;
0519:
0520: Insets insets = scrollPane.getInsets();
0521: initialWidth -= insets.left;
0522: initialWidth -= insets.right;
0523: initialHeight -= insets.top;
0524: initialHeight -= insets.bottom;
0525:
0526: // Subtract 1 from each to avoid adding a scrollbar when we reach
0527: // exactly the width. - e.g. a <table width="100%"> shouldn't
0528: // cause a scrollbar, it should extend right up to the width.
0529: initialWidth--;
0530: initialHeight--;
0531: } else {
0532: Dimension extentSize = viewport.getExtentSize();
0533: initialWidth = extentSize.width;
0534: initialHeight = extentSize.height;
0535: componentVisibleWidth = initialWidth;
0536: componentVisibleHeight = initialHeight;
0537: }
0538: } else {
0539: initialWidth = maxWidth;
0540: }
0541:
0542: if (initialWidth <= 0) {
0543: return;
0544: }
0545:
0546: int wrapWidth = initialWidth;
0547:
0548: // DesignerSettings designerSettings = DesignerSettings.getInstance();
0549: // if (designerSettings.getPageSizeWidth() != -1) {
0550: // int w = designerSettings.getPageSizeWidth();
0551: if (webform.getPageSizeWidth() != -1) {
0552: int w = webform.getPageSizeWidth();
0553:
0554: if (w < wrapWidth) {
0555: wrapWidth = w;
0556: }
0557: }
0558:
0559: relayout(viewport, initialWidth, initialHeight, wrapWidth);
0560:
0561: if (!webform.isGridMode()) {
0562: DesignerPane pane = webform.getPane();
0563:
0564: // if ((pane != null) &&
0565: // ((pane.getCaret() == null) || (pane.getCaretPosition() == Position.NONE))) {
0566: // if ((pane != null) && ((pane.getCaret() == null) || (pane.getCaretPosition() == DomPosition.NONE))) {
0567: if ((pane != null)
0568: && (!pane.hasCaret() || (pane.getCaretDot() == DomPosition.NONE))) {
0569: pane.showCaretAtBeginning();
0570: }
0571: }
0572: }
0573:
0574: protected void updateSizeInfo() {
0575: // The <body> tag needs special handling of margins: unlike all
0576: // other boxes, the margin for the body should be filled in with
0577: // the color of the body box - e.g. act like padding
0578: //width += leftMargin + rightMargin;
0579: //height += effectiveTopMargin + effectiveBottomMargin;
0580: super .updateSizeInfo();
0581:
0582: if (viewport != null) {
0583: // Attempt to preserve the relative scroll position
0584: Point p = viewport.getViewPosition();
0585: Dimension d = viewport.getViewSize();
0586:
0587: // Avoid resize-notification of our own changes
0588: viewport.removeChangeListener(this );
0589:
0590: viewport.setViewSize(new Dimension(width, height));
0591:
0592: // Scroll the view such that it stays relatively in the same place
0593: // but accomodates the new view size
0594: if ((p.x > 0) || (p.y > 0)) {
0595: if (d.width > 0) {
0596: p.x = (p.x * width) / d.width;
0597: } else {
0598: p.x = 0;
0599: }
0600:
0601: if (d.height > 0) {
0602: p.y = (p.y * height) / d.height;
0603: } else {
0604: p.y = 0;
0605: }
0606:
0607: viewport.setViewPosition(p);
0608: }
0609:
0610: // Restore listener
0611: viewport.addChangeListener(this );
0612: }
0613: }
0614:
0615: public void setSize(int width, int height) {
0616: //Log.err.log("************************************************************************************\nPageBox.setSize - width= " + width + "\nLayoutValid was " + layoutValid + " and contextwidth=" + (context != null ? Integer.toString(layoutWidth) : "null"));
0617: if (!layoutValid) {
0618: maxWidth = width;
0619:
0620: //setWidth(width);
0621: //setHeight(height);
0622: //layout();
0623: }
0624: }
0625:
0626: public void relayout(FormatContext context) {
0627: layout();
0628: }
0629:
0630: public void redoLayout(boolean immediate) {
0631: // This seems to be redundant (see only usages in designer/jsf (redoPaneLayout).
0632: //// if (webform.getDomSynchronizer().isRefreshPending()) {
0633: // if (webform.isRefreshPending()) {
0634: // // Change will happen soon anyway
0635: // return;
0636: // }
0637:
0638: // XXX We are not sharing any output window from here.
0639: // InSyncService.getProvider().getRaveErrorHandler().clearErrors(true);
0640: initializeInvariants();
0641: super .redoLayout(immediate);
0642:
0643: if (pane != null) {
0644: pane.repaint();
0645: }
0646: }
0647:
0648: // XXX why is this necessary?
0649: public float getPreferredSpan(int axis) {
0650: if (!layoutValid) {
0651: if (context != null) {
0652: if (viewport != null) {
0653: return (axis == X_AXIS) ? Math.max(layoutWidth,
0654: viewport.getExtentSize().width) : Math.max(
0655: layoutHeight,
0656: viewport.getExtentSize().height);
0657: } else {
0658: return (axis == X_AXIS) ? layoutWidth
0659: : layoutHeight;
0660: }
0661: } else {
0662: return 1;
0663: }
0664: }
0665:
0666: if (layoutValid && (context != null)) {
0667: if (viewport != null) {
0668: return (axis == X_AXIS) ? Math.max(layoutWidth,
0669: viewport.getExtentSize().width) : Math.max(
0670: layoutHeight, viewport.getExtentSize().height);
0671: } else {
0672: return (axis == X_AXIS) ? layoutWidth : layoutHeight;
0673: }
0674: } else {
0675: return (axis == X_AXIS) ? getWidth() : getHeight();
0676: }
0677: }
0678:
0679: /**
0680: * Determines the minimum span for this view along an
0681: * axis.
0682: *
0683: * @param axis may be either <code>View.X_AXIS</code> or
0684: * <code>View.Y_AXIS</code>
0685: * @return the minimum span the view can be rendered into
0686: * @see Box#getPreferredSpan
0687: */
0688: public float getMinimumSpan(int axis) {
0689: return getPreferredSpan(axis);
0690: }
0691:
0692: /**
0693: * Determines the maximum span for this view along an
0694: * axis.
0695: *
0696: * @param axis may be either <code>View.X_AXIS</code> or
0697: * <code>View.Y_AXIS</code>
0698: * @return the maximum span the view can be rendered into
0699: * @see Box#getPreferredSpan
0700: */
0701: public float getMaximumSpan(int axis) {
0702: return getPreferredSpan(axis);
0703: }
0704:
0705: protected String paramString() {
0706: return "PageBox[" + super .paramString() + "]-element="
0707: + getElement();
0708: }
0709:
0710: // FOR DEBUGGING PURPOSES ONLY!
0711: public void setSelected(CssBox box) {
0712: if (box != this .selected) {
0713: this .selected = box;
0714: pane.repaint();
0715: }
0716: }
0717:
0718: public CssBox getSelected() {
0719: return selected;
0720: }
0721:
0722: /** Paint a preview of the page into an image and return it, using
0723: * the specified width and height.
0724: * @todo Turn off the design-time grid, and selections, when painting
0725: * the preview!
0726: * @todo What about the "rendered" fields in XhtmlText and XhtmlElement;
0727: * will my relayout here cause new fragments to be created which
0728: * will change the source pointers away from the designer page's own
0729: * rendered fragments being shown, thus breaking model-to-view (caret)
0730: * mapping etc?
0731: */
0732: public Image createPreviewImage(int width, int height) {
0733: // XXX rename method!!! how about drawPagePreview? Or perhaps drawThumbnail
0734: // since it uses anti aliasing etc.
0735: if (!layoutValid) {
0736: maxWidth = 1024;
0737: }
0738:
0739: // // XXX
0740: // CssBox.noBoxPersistence = true;
0741:
0742: try {
0743: layout();
0744: } catch (Exception e) {
0745: // XXX Why there were eaten all exceptions here?
0746: e.printStackTrace();
0747: return null;
0748: }
0749: // finally {
0750: // CssBox.noBoxPersistence = false;
0751: // }
0752:
0753: if (!layoutValid) {
0754: return null;
0755: }
0756:
0757: Image image = new BufferedImage(width, height,
0758: BufferedImage.TYPE_INT_RGB);
0759:
0760: Graphics2D og = (Graphics2D) image.getGraphics();
0761:
0762: try {
0763: og.setClip(0, 0, width, height);
0764:
0765: //og.setColor(new Color(0, 255, 0, 0));
0766: // Some components are transparent, so we have to
0767: // paint the background. XXX I should ask them so
0768: // I don't have to waste time here!!!
0769: og.setColor(getBackground());
0770: og.fillRect(0, 0, width, height);
0771:
0772: if (this .width > 0) {
0773: // XXX how do I set the clip?
0774: og.setClip(0, 0, this .width, (this .width * height)
0775: / width);
0776:
0777: AffineTransform aT = og.getTransform();
0778:
0779: //og.transform(...);
0780: double scale = width / (double) this .width;
0781:
0782: // TODO - scale such that a really tall page is shown correctly
0783: // too - e.g. pick max or the two scale factors horiz/vert
0784: og.scale(scale, scale);
0785:
0786: // Without antialiasing, the scaled down image looks
0787: // REALLY bad.
0788: og.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
0789: RenderingHints.VALUE_ANTIALIAS_ON);
0790: og.getClipBounds(DesignerPane.clip);
0791: DesignerPane.clipBr.x = DesignerPane.clip.x
0792: + DesignerPane.clip.width;
0793: DesignerPane.clipBr.y = DesignerPane.clip.y
0794: + DesignerPane.clip.height;
0795:
0796: paint(og);
0797: og.setTransform(aT);
0798: }
0799: } finally {
0800: og.dispose();
0801: }
0802:
0803: return image;
0804: }
0805:
0806: /** Paint a preview of the given component, with the given CSS style
0807: * applied, and return it as an image. Use the preferred initial
0808: * width, unless the component is larger.
0809: */
0810: public BufferedImage paintCssPreview(Graphics2D g2d,
0811: String cssStyle,
0812: /*MarkupDesignBean bean,*/Element componentRootElement,
0813: DocumentFragment df, Element element, int initialWidth,
0814: int initialHeight) {
0815: // if (initialWidth == 0) {
0816: // // Ensure that we don't force wrapping on components like a composite
0817: // // breadcrumbs by giving it some space to work with.
0818: // initialWidth = 600;
0819: // }
0820: //
0821: // // Distinguish between the bean we're going to -render- and the one we're
0822: // // going to apply the differente properties to
0823: // MarkupDesignBean renderBean = bean;
0824: //
0825: //// // Handle hyperlinks. We really need to render its surrounding content
0826: //// // to see the CS stylerules for <a> apply
0827: //// if (renderBean.getInstance() instanceof HtmlOutputText) {
0828: //// DesignBean parent = renderBean.getBeanParent();
0829: ////
0830: //// if ((parent != null) && (parent.getChildBeanCount() == 1) &&
0831: //// (parent.getInstance() instanceof HtmlCommandLink ||
0832: //// parent.getInstance() instanceof HtmlOutputLink)) {
0833: //// renderBean = (MarkupDesignBean)parent;
0834: //// }
0835: //// }
0836: ////
0837: //// // Embedded table portions (rowgroups, columns) aren't happy being rendered
0838: //// // without their surrounding table.
0839: //// // It would be better to modify the preview code to actually go and -try- rendering
0840: //// // components and then progressively retry on parents until it succeeds.
0841: //// // But given that the code is freezing today I'm playing it safe
0842: //// if (renderBean.getInstance() instanceof com.sun.rave.web.ui.component.TableColumn
0843: //// || renderBean.getInstance() instanceof com.sun.webui.jsf.component.TableColumn) {
0844: //// if (renderBean.getBeanParent() instanceof MarkupDesignBean) {
0845: //// renderBean = (MarkupDesignBean)renderBean.getBeanParent();
0846: //// } else {
0847: //// return null;
0848: //// }
0849: //// } else if (renderBean.getBeanParent().getInstance() instanceof com.sun.rave.web.ui.component.TableColumn
0850: //// || renderBean.getBeanParent().getInstance() instanceof com.sun.webui.jsf.component.TableColumn) {
0851: //// // We also have to render components that are children of a TableColumn as part of the whole
0852: //// // table as well, because their value binding expressions can involve data providers set up
0853: //// // by the table. This is clearly not a clean solution. See comment above about trying arbitary
0854: //// // rendering instead. This breaks once you nest components in a column inside a container
0855: //// // component for example. Just doing a low risk, 90% fix now right before FCS.
0856: //// if (renderBean.getBeanParent().getBeanParent() instanceof MarkupDesignBean) {
0857: //// renderBean = (MarkupDesignBean)renderBean.getBeanParent().getBeanParent();
0858: //// } else {
0859: //// return null;
0860: //// }
0861: //// }
0862: ////
0863: //// // Not else: a TableColumn can be inside a TableRowGroup so keep moving outwards if necessary:
0864: //// if (renderBean.getInstance() instanceof com.sun.rave.web.ui.component.TableRowGroup
0865: //// || renderBean.getInstance() instanceof com.sun.webui.jsf.component.TableRowGroup) {
0866: //// if (renderBean.getBeanParent() instanceof MarkupDesignBean) {
0867: //// renderBean = (MarkupDesignBean)renderBean.getBeanParent();
0868: //// } else {
0869: //// return null;
0870: //// }
0871: //// }
0872: // // XXX Hack, see the impl.
0873: // renderBean = WebForm.getDomProviderService().adjustRenderBeanHack(renderBean);
0874: //
0875: // Element e = bean.getElement();
0876: // assert e != null;
0877:
0878: setGrid(false); // no grid painting here
0879:
0880: if (!layoutValid) {
0881: maxWidth = 1024;
0882: }
0883:
0884: // // XXX can I shut off errors in output window?
0885: // String oldStyleAttribute = null;
0886: // String oldStyleProperty = null;
0887: //
0888: // if (e.hasAttribute(HtmlAttribute.STYLE)) {
0889: // oldStyleAttribute = e.getAttribute(HtmlAttribute.STYLE);
0890: // }
0891: //
0892: //// XhtmlCssEngine engine = webform.getMarkup().getCssEngine();
0893:
0894: try {
0895: //// engine.setErrorHandler(XhtmlCssEngine.SILENT_ERROR_HANDLER);
0896: //// CssProvider.getEngineService().setSilentErrorHandlerForDocument(webform.getMarkup().getSourceDom());
0897: //// CssProvider.getEngineService().setSilentErrorHandlerForDocument(webform.getMarkup().getRenderedDom());
0898: // CssProvider.getEngineService().setSilentErrorHandlerForDocument(webform.getHtmlDom());
0899: //
0900: //// CssBox.noBoxPersistence = true;
0901: //
0902: // e.setAttribute(HtmlAttribute.STYLE, cssStyle);
0903: //
0904: // DesignProperty prop = bean.getProperty("style");
0905: //
0906: // if (prop != null) {
0907: // oldStyleProperty = (String)prop.getValue();
0908: //
0909: // try {
0910: // Method m = prop.getPropertyDescriptor().getWriteMethod();
0911: // m.invoke(bean.getInstance(), new Object[] { cssStyle });
0912: // } catch (Exception ex) {
0913: // ErrorManager.getDefault().notify(ex);
0914: // }
0915: // }
0916: //
0917: //// engine.clearComputedStyles(e, "");
0918: // CssProvider.getEngineService().clearComputedStylesForElement(e);
0919:
0920: CreateContext cc = new CreateContext();
0921: cc.pushPage(webform);
0922:
0923: // Font font = CssLookup.getFont(body, DesignerSettings.getInstance().getDefaultFontSize());
0924: // Font font = CssProvider.getValueService().getFontForElement(body, DesignerSettings.getInstance().getDefaultFontSize(), Font.PLAIN);
0925: // cc.metrics = Toolkit.getDefaultToolkit().getFontMetrics(font);
0926: // XXX Missing text.
0927: cc.metrics = CssUtilities.getDesignerFontMetricsForElement(
0928: body, null, webform.getDefaultFontSize());
0929:
0930: // // Try to render JSF so I can process the DF before proceeding
0931: // Element element = renderBean.getElement();
0932: // String tagName = element.getTagName();
0933: // HtmlTag tag = HtmlTag.getTag(tagName);
0934: //
0935: // if (tag == null) {
0936: // // Possibly a Jsf component.
0937: // // Use getDocument() rather than doc directly since
0938: // // e.g. jsp includes may point to external documents here,
0939: // // not the document containing the jsp tag itself
0940: //
0941: // // XXX TODO There is not needed webform here.
0942: //// FileObject markupFile = webform.getModel().getMarkupFile();
0943: ////// DocumentFragment df = FacesSupport.renderHtml(markupFile, renderBean, !CssBox.noBoxPersistence);
0944: //// DocumentFragment df = InSyncService.getProvider().renderHtml(markupFile, renderBean);
0945: // DocumentFragment df = webform.renderHtmlForMarkupDesignBean(renderBean);
0946:
0947: // XXX FIXME Is this correct here?
0948: // This was needless here:
0949: // 1) the previously called getHtmlBody has this side effect.
0950: // 2) there shouldn't be any component associated with this.
0951: // webform.updateErrorsInComponent();
0952:
0953: if (df != null) {
0954: // XXX Moved to designer/jsf/../DesignerServiceHackImpl.
0955: // DesignerUtils.stripDesignStyleClasses(df);
0956:
0957: // Yes - add its nodes into our box list
0958: NodeList nl = df.getChildNodes();
0959: int num = nl.getLength();
0960: setProbableChildCount(num); // or addProbablyChildCount??
0961:
0962: for (int i = 0, n = num; i < n; i++) {
0963: Node nn = nl.item(i); // Recurse
0964:
0965: if ((nn.getNodeType() == Node.TEXT_NODE)
0966: && COLLAPSE
0967: && DesignerUtils.onlyWhitespace(nn
0968: .getNodeValue())) {
0969: continue;
0970: }
0971:
0972: addNode(cc, nn, element, null, null);
0973: }
0974: } else {
0975: // Not a JSF component -- normal processing
0976: addNode(cc, element, element, null, null);
0977: }
0978: // } else {
0979: // // Not a JSF component -- normal processing
0980: // addNode(cc, element, element, null, null);
0981: // }
0982:
0983: int wrapWidth = initialWidth;
0984: relayout(null, initialWidth, initialHeight, wrapWidth);
0985: } catch (Exception ex) {
0986: ErrorManager.getDefault().notify(ex);
0987:
0988: return null;
0989: }
0990: // } finally {
0991: //// CssBox.noBoxPersistence = false;
0992: //
0993: // if (oldStyleAttribute != null) {
0994: // e.setAttribute(HtmlAttribute.STYLE, oldStyleAttribute);
0995: // } else {
0996: // e.removeAttribute(HtmlAttribute.STYLE);
0997: // }
0998: //
0999: // DesignProperty prop = bean.getProperty("style");
1000: //
1001: // if (prop != null) {
1002: // try {
1003: // Method m = prop.getPropertyDescriptor().getWriteMethod();
1004: // m.invoke(bean.getInstance(), new Object[] { oldStyleProperty });
1005: // } catch (Exception ex) {
1006: // ErrorManager.getDefault().notify(ex);
1007: // }
1008: // }
1009: //
1010: //// engine.clearComputedStyles(e, null);
1011: // CssEngineService cssEngineService = CssProvider.getEngineService();
1012: // cssEngineService.clearComputedStylesForElement(e);
1013: //
1014: // if (renderBean != bean) {
1015: //// engine.clearComputedStyles(renderBean.getElement(), null);
1016: // cssEngineService.clearComputedStylesForElement(renderBean.getElement());
1017: // }
1018: //
1019: //// engine.setErrorHandler(null);
1020: //// cssEngineService.setNullErrorHandlerForDocument(webform.getMarkup().getSourceDom());
1021: //// cssEngineService.setNullErrorHandlerForDocument(webform.getMarkup().getRenderedDom());
1022: // cssEngineService.setNullErrorHandlerForDocument(webform.getHtmlDom());
1023: // }
1024:
1025: if (!layoutValid) {
1026: return null;
1027: }
1028:
1029: // CssBox box = findCssBox(bean);
1030: // CssBox box = findCssBoxForComponentRootElement(
1031: // WebForm.getDomProviderService().getComponentRootElementForMarkupDesignBean(bean));
1032: CssBox box = findCssBoxForComponentRootElement(componentRootElement);
1033: // XXX This would provide the changes in the style, but don't inherit/lays out the correct sizes,
1034: // Needed to be investigated (on the other hand this method is all hack).
1035: // CssBox box = findCssBoxForComponentRootElement(element);
1036: // if (box == null) {
1037: // // XXX The new one was not found, use the old one.
1038: // box = findCssBoxForComponentRootElement(componentRootElement);
1039: // }
1040:
1041: Rectangle bounds;
1042:
1043: if (box == null) {
1044: // bounds = computeBounds(bean, null);
1045: // bounds = computeBounds(WebForm.getDomProviderService().getComponentRootElementForMarkupDesignBean(bean), null);
1046: bounds = computeBounds(componentRootElement, null);
1047: // XXX #6389428 Possible NPE. Probably just a consequence of some other issue.
1048: if (bounds == null) {
1049: // Log it?
1050: return null;
1051: }
1052:
1053: width = bounds.width;
1054: height = bounds.height;
1055: box = this ;
1056: } else {
1057: width = box.getWidth();
1058: height = box.getHeight();
1059: bounds = new Rectangle(0, 0, width, height);
1060: }
1061:
1062: // See what the computed size is
1063: if ((width <= 0) || (width >= 3000)) {
1064: return null;
1065: }
1066:
1067: if ((height <= 0) || (height >= 3000)) {
1068: return null;
1069: }
1070:
1071: // Restore?
1072: BufferedImage image = null;
1073:
1074: if (g2d != null) {
1075: GraphicsConfiguration config = g2d.getDeviceConfiguration();
1076: image = config.createCompatibleImage(width, height);
1077: } else {
1078: image = new BufferedImage(width, height,
1079: BufferedImage.TYPE_INT_RGB);
1080: }
1081:
1082: if (image != null) {
1083: Graphics2D og = (Graphics2D) image.getGraphics();
1084:
1085: try {
1086: // I have to set a clip rectangle here. That's usually done by
1087: // Swing (when we're painting the designer surface), but not
1088: // when I'm obtaining a Graphics object as shown above.
1089: // And this matters because the BackgroundImagePainter looks
1090: // to see if a clip is in effect, and if there's no clip, it
1091: // doesn't use its own clipping rectangle - with the net result
1092: // that the background painting can overflow its background area
1093: // (and cover up borders etc.)
1094: og.setClip(0, 0, width, height);
1095:
1096: //og.setColor(new Color(0, 255, 0, 0));
1097: // Some components are transparent, so we have to
1098: // paint the background. XXX I should ask them so
1099: // I don't have to waste time here!!!
1100: og.setColor(getBackground());
1101: og.fillRect(0, 0, width, height);
1102:
1103: if (this .width > 0) {
1104: // Turn off clip optimizations since they depend on box extents
1105: // which we're not recomputing for this box
1106: boolean oldClip = DesignerPane.INCREMENTAL_LAYOUT;
1107: DesignerPane.INCREMENTAL_LAYOUT = false;
1108:
1109: int oldX = box.getX();
1110: int oldY = box.getY();
1111: int oldLeftMargin = box.getLeftMargin();
1112: int oldEffectiveTopMargin = box
1113: .getEffectiveTopMargin();
1114:
1115: box.setLocation(0, 0);
1116: box.setMargins(0, 0);
1117:
1118: if (box == this ) {
1119: context.initialCB.x = -bounds.x;
1120: context.initialCB.y = -bounds.y;
1121: }
1122:
1123: try {
1124: box.paint(og, -bounds.x, -bounds.y);
1125: } finally {
1126: DesignerPane.INCREMENTAL_LAYOUT = oldClip;
1127: box.setLocation(oldX, oldY);
1128: box.setMargins(oldLeftMargin,
1129: oldEffectiveTopMargin);
1130: }
1131:
1132: //og.setTransform(aT);
1133: }
1134: } finally {
1135: og.dispose();
1136: }
1137: }
1138:
1139: return image;
1140: }
1141:
1142: // /**
1143: // * Provides a mapping, for a given character,
1144: // * from the document model coordinate space
1145: // * to the view coordinate space.
1146: // *
1147: // * @todo Find a better home for this method
1148: // * @param pos the position of the desired character (>=0)
1149: // * @return the bounding box, in view coordinate space,
1150: // * of the character at the specified position
1151: // * @see View#viewToModel
1152: // * @todo Replace with mapper usage!
1153: // */
1154: // public Rectangle modelToView(Position pos) {
1155: // return ModelViewMapper.modelToView(this, pos);
1156: // }
1157:
1158: /** Return the deepest box containing the given point. */
1159: public CssBox findCssBox(int x, int y) { // XXX return Box instead?
1160:
1161: CssBox box = findCssBox(x, y, leftMargin, effectiveTopMargin, 0);
1162:
1163: if (box == null) {
1164: box = this ;
1165: }
1166:
1167: return box;
1168: }
1169:
1170: /** Initialize whether this box should show a visual grid and should receive
1171: * grid mode handling from mouse operations */
1172: protected void initializeGrid() {
1173: // if (webform.getDocument().isGridMode()) {
1174: // if (webform.isGridModeDocument()) {
1175: if (webform.isGridMode()) {
1176: setGrid(true);
1177: }
1178: }
1179:
1180: protected void initializeBackground() {
1181: super .initializeBackground();
1182:
1183: if (bg == null) {
1184: bg = Color.white;
1185: }
1186:
1187: dark = ColorManager.isDark(bg);
1188: }
1189:
1190: /** Report whether the background on this page is "dark".
1191: * In this case for example the selection markers should
1192: * not be painted in black but in some light/reverse video colors.
1193: */
1194: public boolean isDarkBackground() {
1195: return dark;
1196: }
1197: }
|