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.api.designer.cssengine.CssProvider;
0044: import org.netbeans.modules.visualweb.api.designer.cssengine.CssValue;
0045: import org.netbeans.modules.visualweb.designer.CssUtilities;
0046: import java.awt.BasicStroke;
0047: import java.awt.Color;
0048: import java.awt.Component;
0049: import java.awt.Graphics;
0050: import java.awt.Graphics2D;
0051: import java.awt.Insets;
0052: import java.awt.Stroke;
0053:
0054: import org.w3c.dom.Element;
0055:
0056: import org.netbeans.modules.visualweb.api.designer.cssengine.XhtmlCss;
0057:
0058: /**
0059: * A border which paints according to CSS2 specifications. In particular,
0060: * the border brush may have different thickness for the top, bottom,
0061: * left and right edges, and each edge may have a different style, such
0062: * as solid, dashed, dotted, or inset.
0063: *
0064: * @todo Consider supporting the proposed CSS3 border-radius property.
0065: * http://www.w3.org/TR/2002/WD-css3-border-20021107/#the-border-radius
0066: * The mozilla one is -moz-border-radius (-topright, -topleft, -bottomright,
0067: * -bottomleft). The Braveheart stylesheets already use this property to
0068: * get rounded buttons.
0069: *
0070: * @author Tor Norbye
0071: */
0072: public class CssBorder {
0073: /** No sides */
0074: public static final int FRAME_VOID = 0;
0075:
0076: /** Side above box */
0077: public static final int FRAME_TOP = 1;
0078:
0079: /** Side below box */
0080: public static final int FRAME_BOTTOM = 2;
0081:
0082: /** Left-hand-side of box */
0083: public static final int FRAME_LEFT = 4;
0084:
0085: /** Right-hand-side of box */
0086: public static final int FRAME_RIGHT = 8;
0087:
0088: /** All sides */
0089: public static final int FRAME_BOX = FRAME_TOP | FRAME_BOTTOM
0090: | FRAME_LEFT | FRAME_RIGHT;
0091:
0092: /** Same as all sides, but used for the case where we haven't specifically set
0093: * the frame/rules property, so I can tell "all" from "not set". */
0094: public static final int FRAME_UNSET = FRAME_BOX | 128;
0095:
0096: /* Now defined in BorderWidthManager
0097: // Border thickness - these are the values Mozilla 1.5 empirically
0098: // seems to use (on Solaris, hopefully not platform specific)
0099: // In the working draft for the CSS3 Box Model, they mention UA's
0100: // could make it depend on the font size, e.g. use the below sizes
0101: // when the font size is less than 17pt, and bump them up for bigger
0102: // fonts.
0103: public static final int WIDTH_THICK = 5;
0104: */
0105: public static final int WIDTH_THIN = 1;
0106: public static final int WIDTH_MEDIUM = 3;
0107: static final int STYLE_UNKNOWN = -1;
0108: static final int STYLE_NONE = 0;
0109: static final int STYLE_SOLID = 1;
0110: static final int STYLE_DASHED = 2;
0111: static final int STYLE_DOTTED = 3;
0112: static final int STYLE_DOUBLE = 4;
0113: static final int STYLE_GROOVE = 5;
0114: static final int STYLE_RIDGE = 6;
0115: static final int STYLE_INSET = 7;
0116: static final int STYLE_OUTSET = 8;
0117: private static CssBorder designerBorder = null;
0118: private static CssBorder emptyBorder = null;
0119: private static final Color TRANSPARENT = new Color(0, 0, 0);
0120: private final Color topColor;
0121: private final Color bottomColor;
0122: private final Color leftColor;
0123: private final Color rightColor;
0124: private Color topAltColor;
0125: private Color bottomAltColor;
0126: private Color leftAltColor;
0127: private Color rightAltColor;
0128: private Stroke topStroke;
0129: private Stroke bottomStroke;
0130: private Stroke leftStroke;
0131: private Stroke rightStroke;
0132: private final int topWidth;
0133: private final int bottomWidth;
0134: private final int rightWidth;
0135: private final int leftWidth;
0136: private Stroke prevStroke = null;
0137: private int prevWidth = -1;
0138: private int prevStyle = -1;
0139: int[] pointsX = new int[4];
0140: int[] pointsY = new int[4];
0141:
0142: /**
0143: * Don't use directly - see static getBorder() factory method.
0144: * @todo Improve color computations here to do the right thing when
0145: * I have a fully saturated (or unsaturated) color. Also see
0146: * HtmlLabelUI for some color computation code which might be
0147: * interesting.
0148: */
0149: private CssBorder(int topWidth, int bottomWidth, int rightWidth,
0150: int leftWidth, int topStyle, int rightStyle,
0151: int bottomStyle, int leftStyle, Color topColor,
0152: Color bottomColor, Color rightColor, Color leftColor) {
0153: this .topWidth = topWidth;
0154: this .bottomWidth = bottomWidth;
0155: this .leftWidth = leftWidth;
0156: this .rightWidth = rightWidth;
0157:
0158: // Reuse strokes when possible
0159: if ((leftStyle == STYLE_SOLID) || (leftStyle == STYLE_DOUBLE)) {
0160: // Use color as-is
0161: } else if (leftStyle == STYLE_INSET) {
0162: leftColor = leftColor.darker();
0163: } else if (leftStyle == STYLE_OUTSET) {
0164: leftColor = leftColor.brighter();
0165: } else if (leftStyle == STYLE_RIDGE) {
0166: leftAltColor = leftColor.darker();
0167: leftColor = leftColor.brighter();
0168: } else if (leftStyle == STYLE_GROOVE) {
0169: leftAltColor = leftColor.brighter();
0170: leftColor = leftColor.darker();
0171: } else {
0172: leftStroke = getStroke(leftWidth, leftStyle);
0173: }
0174:
0175: if ((topStyle == STYLE_SOLID) || (topStyle == STYLE_DOUBLE)) {
0176: // Use color as-is
0177: } else if (topStyle == STYLE_INSET) {
0178: topColor = topColor.darker();
0179: } else if (topStyle == STYLE_OUTSET) {
0180: topColor = topColor.brighter();
0181: } else if (topStyle == STYLE_RIDGE) {
0182: topAltColor = topColor.darker();
0183: topColor = topColor.brighter();
0184: } else if (topStyle == STYLE_GROOVE) {
0185: topAltColor = topColor.brighter();
0186: topColor = topColor.darker();
0187: } else if (topStyle != STYLE_NONE) {
0188: topStroke = getStroke(topWidth, topStyle);
0189: }
0190:
0191: if ((rightStyle == STYLE_SOLID) || (rightStyle == STYLE_DOUBLE)) {
0192: // Use color as-is
0193: } else if (rightStyle == STYLE_INSET) {
0194: rightColor = rightColor.brighter();
0195: } else if (rightStyle == STYLE_OUTSET) {
0196: rightColor = rightColor.darker();
0197: } else if (rightStyle == STYLE_RIDGE) {
0198: rightAltColor = rightColor.brighter();
0199: rightColor = rightColor.darker();
0200: } else if (rightStyle == STYLE_GROOVE) {
0201: rightAltColor = rightColor.darker();
0202: rightColor = rightColor.brighter();
0203: } else if (rightStyle != STYLE_NONE) {
0204: rightStroke = getStroke(rightWidth, rightStyle);
0205: }
0206:
0207: if ((bottomStyle == STYLE_SOLID)
0208: || (bottomStyle == STYLE_DOUBLE)) {
0209: // Use color as-is
0210: } else if (bottomStyle == STYLE_INSET) {
0211: bottomColor = bottomColor.brighter();
0212: } else if (bottomStyle == STYLE_OUTSET) {
0213: bottomColor = bottomColor.darker();
0214: } else if (bottomStyle == STYLE_RIDGE) {
0215: bottomAltColor = bottomColor.brighter();
0216: bottomColor = bottomColor.darker();
0217: } else if (bottomStyle == STYLE_GROOVE) {
0218: bottomAltColor = bottomColor.darker();
0219: bottomColor = bottomColor.brighter();
0220: } else {
0221: bottomStroke = getStroke(bottomWidth, bottomStyle);
0222: }
0223:
0224: // TODO [PERFORMANCE] - try to "share" strokes and colors here
0225: // when edges have the same original colors, widths
0226: // or styles.
0227:
0228: this .leftColor = leftColor;
0229: this .rightColor = rightColor;
0230: this .bottomColor = bottomColor;
0231: this .topColor = topColor;
0232: }
0233:
0234: /* (This comment might be obsolete, I might have done this already)
0235: TODO: force widths to 0 if no style has been set
0236:
0237: From http://web.oreilly.com/news/csstop10_0500.html:
0238:
0239: 7) Borders without style don't exist. Since the default border-style is
0240: none, if you don't explicitly declare a border-style for a border then
0241: it will have no existence at all, and therefore zero width. After all,
0242: something can have width only if it exists. If you want extra space to
0243: appear around an element such as an image, use margins, not a border
0244: width.
0245: */
0246:
0247: /** Return a border capable of painting the given element's border
0248: * preferences. The top, bottom, right and left parameters list
0249: * the border thickness to use for each edge. (These are looked up
0250: * by the box (the client of this class) rather than here in the
0251: * border since the border dimensions are part of the geometry of
0252: * the box.) May return null if no border should be painted.
0253: */
0254: public static CssBorder getBorder(Element element) {
0255: return getBorder(element, -1, STYLE_NONE, FRAME_BOX);
0256: }
0257:
0258: /** Same as {@link getBorder(Document, Element)} but allows you
0259: * to specify a default width and style which will be used for
0260: * any border edge that does not have an explicit CSS property
0261: * setting. This is used to force table cells to take on the
0262: * table element's border attribute width, for example.
0263: * @param defaultWidth The default width to use, or -1 if a
0264: * default border width should not be set.
0265: * @param defaultStyle The default style to use for edges that
0266: * do not specify a border, or STYLE_NONE.
0267: */
0268: public static CssBorder getBorder(Element element,
0269: int defaultWidth, int defaultStyle, int mask) {
0270: // Set to -1 so we can detect later that one wasn't set
0271: int leftStyle = getBorderStyle(element,
0272: XhtmlCss.BORDER_LEFT_STYLE_INDEX);
0273: int rightStyle = getBorderStyle(element,
0274: XhtmlCss.BORDER_RIGHT_STYLE_INDEX);
0275: int topStyle = getBorderStyle(element,
0276: XhtmlCss.BORDER_TOP_STYLE_INDEX);
0277: int bottomStyle = getBorderStyle(element,
0278: XhtmlCss.BORDER_BOTTOM_STYLE_INDEX);
0279:
0280: if (leftStyle == STYLE_UNKNOWN) {
0281: if ((mask & FRAME_LEFT) != 0) {
0282: leftStyle = defaultStyle;
0283: } else {
0284: leftStyle = STYLE_NONE;
0285: }
0286: }
0287:
0288: if (rightStyle == STYLE_UNKNOWN) {
0289: if ((mask & FRAME_RIGHT) != 0) {
0290: rightStyle = defaultStyle;
0291: } else {
0292: rightStyle = STYLE_NONE;
0293: }
0294: }
0295:
0296: if (topStyle == STYLE_UNKNOWN) {
0297: if ((mask & FRAME_TOP) != 0) {
0298: topStyle = defaultStyle;
0299: } else {
0300: topStyle = STYLE_NONE;
0301: }
0302: }
0303:
0304: if (bottomStyle == STYLE_UNKNOWN) {
0305: if ((mask & FRAME_BOTTOM) != 0) {
0306: bottomStyle = defaultStyle;
0307: } else {
0308: bottomStyle = STYLE_NONE;
0309: }
0310: }
0311:
0312: if ((topStyle == STYLE_NONE) && (bottomStyle == STYLE_NONE)
0313: && (leftStyle == STYLE_NONE)
0314: && (rightStyle == STYLE_NONE) && (mask == FRAME_VOID)) { // XXX IS THIS RIGHT?
0315:
0316: return null;
0317: }
0318:
0319: // Section 10.3.3 indicates that a border-style of "none" should
0320: // use 0 as the border width. Is that correct?
0321: int topWidth;
0322:
0323: // Section 10.3.3 indicates that a border-style of "none" should
0324: // use 0 as the border width. Is that correct?
0325: int bottomWidth;
0326:
0327: // Section 10.3.3 indicates that a border-style of "none" should
0328: // use 0 as the border width. Is that correct?
0329: int leftWidth;
0330:
0331: // Section 10.3.3 indicates that a border-style of "none" should
0332: // use 0 as the border width. Is that correct?
0333: int rightWidth;
0334:
0335: if (topStyle == STYLE_NONE) {
0336: topWidth = 0;
0337: } else {
0338: topWidth = getBorderWidth(element,
0339: XhtmlCss.BORDER_TOP_WIDTH_INDEX, defaultWidth);
0340: }
0341:
0342: if (bottomStyle == STYLE_NONE) {
0343: bottomWidth = 0;
0344: } else {
0345: bottomWidth = getBorderWidth(element,
0346: XhtmlCss.BORDER_BOTTOM_WIDTH_INDEX, defaultWidth);
0347: }
0348:
0349: if (leftStyle == STYLE_NONE) {
0350: leftWidth = 0;
0351: } else {
0352: leftWidth = getBorderWidth(element,
0353: XhtmlCss.BORDER_LEFT_WIDTH_INDEX, defaultWidth);
0354: }
0355:
0356: if (rightStyle == STYLE_NONE) {
0357: rightWidth = 0;
0358: } else {
0359: rightWidth = getBorderWidth(element,
0360: XhtmlCss.BORDER_RIGHT_WIDTH_INDEX, defaultWidth);
0361: }
0362:
0363: // Border widths have to be positive - TODO - check and enforce this
0364: // If a border-style is set the width defaults to medium, not zero.
0365: if (topWidth == -1) {
0366: if (topStyle == STYLE_NONE) {
0367: topWidth = 0;
0368: } else {
0369: topWidth = WIDTH_MEDIUM;
0370: }
0371: }
0372:
0373: if (bottomWidth == -1) {
0374: if (bottomStyle == STYLE_NONE) {
0375: bottomWidth = 0;
0376: } else {
0377: bottomWidth = WIDTH_MEDIUM;
0378: }
0379: }
0380:
0381: if (rightWidth == -1) {
0382: if (rightStyle == STYLE_NONE) {
0383: rightWidth = 0;
0384: } else {
0385: rightWidth = WIDTH_MEDIUM;
0386: }
0387: }
0388:
0389: if (leftWidth == -1) {
0390: if (leftStyle == STYLE_NONE) {
0391: leftWidth = 0;
0392: } else {
0393: leftWidth = WIDTH_MEDIUM;
0394: }
0395: }
0396:
0397: // See if we already know we can bail
0398: if ((topWidth == 0) && (bottomWidth == 0) && (leftWidth == 0)
0399: && (rightWidth == 0)) {
0400: return null;
0401: }
0402:
0403: // Look up colors
0404: Color topColor = null;
0405: Color bottomColor = null;
0406: Color rightColor = null;
0407: Color leftColor = null;
0408:
0409: if (leftWidth != 0) { // no point looking for style when 0-sized
0410: leftColor = getColor(element,
0411: XhtmlCss.BORDER_LEFT_COLOR_INDEX);
0412: }
0413:
0414: if (rightWidth > 0) {
0415: rightColor = getColor(element,
0416: XhtmlCss.BORDER_RIGHT_COLOR_INDEX);
0417: }
0418:
0419: if (topWidth > 0) {
0420: topColor = getColor(element,
0421: XhtmlCss.BORDER_TOP_COLOR_INDEX);
0422: }
0423:
0424: if (bottomWidth > 0) {
0425: bottomColor = getColor(element,
0426: XhtmlCss.BORDER_BOTTOM_COLOR_INDEX);
0427: }
0428:
0429: if ((topWidth > 0) && (topStyle != STYLE_NONE)
0430: && (topColor == null)) {
0431: topColor = getDefaultColor(element);
0432: }
0433:
0434: if ((bottomWidth > 0) && (bottomStyle != STYLE_NONE)
0435: && (bottomColor == null)) {
0436: bottomColor = getDefaultColor(element);
0437: }
0438:
0439: if ((leftWidth > 0) && (leftStyle != STYLE_NONE)
0440: && (leftColor == null)) {
0441: leftColor = getDefaultColor(element);
0442: }
0443:
0444: if ((rightWidth > 0) && (rightStyle != STYLE_NONE)
0445: && (rightColor == null)) {
0446: rightColor = getDefaultColor(element);
0447: }
0448:
0449: if (topColor == TRANSPARENT) {
0450: topStyle = STYLE_NONE;
0451: }
0452:
0453: if (bottomColor == TRANSPARENT) {
0454: bottomStyle = STYLE_NONE;
0455: }
0456:
0457: if (leftColor == TRANSPARENT) {
0458: leftStyle = STYLE_NONE;
0459: }
0460:
0461: if (rightColor == TRANSPARENT) {
0462: rightStyle = STYLE_NONE;
0463: }
0464:
0465: // Check again in case we've set transparent colors to the point
0466: // that we don't need any borders
0467: if ((topStyle == STYLE_NONE) && (bottomStyle == STYLE_NONE)
0468: && (leftStyle == STYLE_NONE)
0469: && (rightStyle == STYLE_NONE)) {
0470: return null;
0471: }
0472:
0473: // XXX #126609 NPE.
0474: if (topColor == null) {
0475: topColor = getDefaultColor(element);
0476: }
0477: if (bottomColor == null) {
0478: bottomColor = getDefaultColor(element);
0479: }
0480: if (leftColor == null) {
0481: leftColor = getDefaultColor(element);
0482: }
0483: if (rightColor == null) {
0484: rightColor = getDefaultColor(element);
0485: }
0486:
0487: return new CssBorder(topWidth, bottomWidth, rightWidth,
0488: leftWidth, topStyle, rightStyle, bottomStyle,
0489: leftStyle, topColor, bottomColor, rightColor, leftColor);
0490: }
0491:
0492: private static int getBorderWidth(Element element, int property,
0493: int defaultWidth) {
0494: // CSS will return the default width as a value - e.g. "medium",
0495: // but in case the user has defined anything I want to use my own
0496: // default width not from the stylesheet (for example, I want to
0497: // apply the "border" attribute's value unless a specific css
0498: // property has been set)
0499: if (defaultWidth != -1) {
0500: // CSSEngine engine = CssLookup.getCssEngine(element);
0501: // if (engine.isDefaultValue((CSSStylableElement)element, null, property)) {
0502: if (CssProvider.getEngineService()
0503: .isDefaultStyleValueForElement(element, null,
0504: property)) {
0505: return defaultWidth;
0506: }
0507: }
0508:
0509: // int len = CssLookup.getLength(element, property);
0510: int len = CssUtilities.getCssLength(element, property);
0511:
0512: if (len < 0) {
0513: len = 0;
0514: }
0515:
0516: return len;
0517: }
0518:
0519: private static Color getColor(Element element, int propidx) {
0520: // return CssLookup.getColor(element, propidx);
0521: return CssProvider.getValueService().getColorForElement(
0522: element, propidx);
0523: }
0524:
0525: /** Return default color to use for a border span if one wasn't
0526: * specified by the stylesheet. As per the spec, this reverts
0527: * to using the color attribute on the element. */
0528: private static Color getDefaultColor(Element element) {
0529: // XXX cache this to avoid repeated lookups? Probably worthwhile
0530: // since color lookup is a bit expensive.
0531: // Color defaultColor = CssLookup.getColor(element, XhtmlCss.COLOR_INDEX);
0532: Color defaultColor = CssProvider.getValueService()
0533: .getColorForElement(element, XhtmlCss.COLOR_INDEX);
0534:
0535: if (defaultColor == null) { // shouldn't happen
0536: defaultColor = Color.black;
0537: }
0538:
0539: return defaultColor;
0540: }
0541:
0542: private static int getBorderStyle(Element element, int property) {
0543: // CSSEngine engine = CssLookup.getCssEngine(element);
0544: // if (engine.isDefaultValue((CSSStylableElement)element, null, property)) {
0545: if (CssProvider.getEngineService()
0546: .isDefaultStyleValueForElement(element, null, property)) {
0547: return STYLE_UNKNOWN;
0548: }
0549:
0550: // Value val = CssLookup.getValue(element, property);
0551: CssValue cssValue = CssProvider.getEngineService()
0552: .getComputedValueForElement(element, property);
0553: // if (CssValueConstants.NONE_VALUE == val) {
0554: if (CssProvider.getValueService().isNoneValue(cssValue)) {
0555: return STYLE_NONE;
0556:
0557: // XXX This is not right! How do I solve this? I need
0558: // to know whether or not a border has not been set
0559: // locally on the element so I can apply a default style
0560: // in that case, but if the border-style has actually
0561: // been SET to "none", I should be returning STYLE_NONE!
0562: //return STYLE_UNKNOWN;
0563: // } else if (CssValueConstants.HIDDEN_VALUE == val) {
0564: } else if (CssProvider.getValueService()
0565: .isHiddenValue(cssValue)) {
0566: return STYLE_NONE;
0567: // } else if (CssValueConstants.DOTTED_VALUE == val) {
0568: } else if (CssProvider.getValueService()
0569: .isDottedValue(cssValue)) {
0570: return STYLE_DOTTED;
0571: // } else if (CssValueConstants.DASHED_VALUE == val) {
0572: } else if (CssProvider.getValueService()
0573: .isDashedValue(cssValue)) {
0574: return STYLE_DASHED;
0575: // } else if (CssValueConstants.SOLID_VALUE == val) {
0576: } else if (CssProvider.getValueService().isSolidValue(cssValue)) {
0577: return STYLE_SOLID;
0578: // } else if (CssValueConstants.DOUBLE_VALUE == val) {
0579: } else if (CssProvider.getValueService()
0580: .isDoubleValue(cssValue)) {
0581: return STYLE_DOUBLE;
0582: // } else if (CssValueConstants.GROOVE_VALUE == val) {
0583: } else if (CssProvider.getValueService()
0584: .isGrooveValue(cssValue)) {
0585: return STYLE_GROOVE;
0586: // } else if (CssValueConstants.RIDGE_VALUE == val) {
0587: } else if (CssProvider.getValueService().isRidgeValue(cssValue)) {
0588: return STYLE_RIDGE;
0589: // } else if (CssValueConstants.INSET_VALUE == val) {
0590: } else if (CssProvider.getValueService().isInsetValue(cssValue)) {
0591: return STYLE_INSET;
0592: // } else if (CssValueConstants.OUTSET_VALUE == val) {
0593: } else if (CssProvider.getValueService()
0594: .isOutsetValue(cssValue)) {
0595: return STYLE_OUTSET;
0596: }
0597:
0598: return STYLE_UNKNOWN;
0599: }
0600:
0601: /** Return a "designer" border - a border suitable for showing
0602: * a selection around a box, distinguishable from a typical
0603: * document box. The border will therefore probably be in
0604: * some light color, with a dashed pattern. */
0605: public static CssBorder getDesignerBorder() {
0606: if (designerBorder == null) {
0607: Color color = Color.lightGray;
0608: designerBorder = new CssBorder(1, 1, 1, 1,
0609: //STYLE_SOLID,STYLE_SOLID,STYLE_SOLID,STYLE_SOLID,
0610: STYLE_DASHED, STYLE_DASHED, STYLE_DASHED,
0611: STYLE_DASHED, color, color, color, color);
0612: }
0613:
0614: return designerBorder;
0615: }
0616:
0617: /**
0618: * Return a border of the given style, width and color.
0619: * Style should be one of STYLE_INSET, etc.
0620: *
0621: */
0622: static CssBorder getBorder(int style, int width, Color color) {
0623: return new CssBorder(width, width, width, width, style, style,
0624: style, style, color, color, color, color);
0625: }
0626:
0627: public static CssBorder getEmptyBorder() {
0628: if (emptyBorder == null) {
0629: emptyBorder = new CssBorder(0, 0, 0, 0, STYLE_NONE,
0630: STYLE_NONE, STYLE_NONE, STYLE_NONE, Color.black,
0631: Color.black, Color.black, Color.black);
0632: }
0633:
0634: return emptyBorder;
0635: }
0636:
0637: private Stroke getStroke(int width, int style) {
0638: if (width == 0) {
0639: return null;
0640: }
0641:
0642: if ((prevWidth == width) && (prevStyle == style)) {
0643: return prevStroke;
0644: }
0645:
0646: Stroke stroke = null;
0647:
0648: switch (style) {
0649: case STYLE_UNKNOWN:
0650: case STYLE_NONE:
0651: break;
0652:
0653: case STYLE_DASHED:
0654: stroke = new BasicStroke((float) width,
0655: BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER,
0656: 10.0f,
0657: new float[] { 6 * width, (6 * width) + width },
0658: 0.0f);
0659:
0660: break;
0661:
0662: case STYLE_DOTTED:
0663: stroke = new BasicStroke((float) width,
0664: BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER,
0665: 10.0f, new float[] { width, 3 * width }, 0.0f);
0666:
0667: break;
0668:
0669: case STYLE_SOLID:
0670: case STYLE_DOUBLE:
0671: case STYLE_GROOVE:
0672: case STYLE_RIDGE:
0673: case STYLE_INSET:
0674: case STYLE_OUTSET:
0675:
0676: // Special handling in the painter
0677: break;
0678: }
0679:
0680: prevStroke = stroke; // cache for next time
0681: prevStyle = style;
0682: prevWidth = width;
0683:
0684: return stroke;
0685: }
0686:
0687: /**
0688: * Paints the border for the specified component with the specified
0689: * position and size.
0690: * @param c the component for which this border is being painted
0691: * @param g the paint graphics
0692: * @param x the x position of the painted border
0693: * @param y the y position of the painted border
0694: * @param width the width of the painted border
0695: * @param height the height of the painted border
0696: */
0697: public void paintBorder(Graphics g, int x, int y, int width,
0698: int height) {
0699: Graphics2D g2d = (Graphics2D) g;
0700:
0701: Color oldColor = g.getColor();
0702: Stroke oldStroke = g2d.getStroke();
0703:
0704: if ((topStroke == bottomStroke)
0705: && (bottomStroke == rightStroke)
0706: && (rightStroke == leftStroke)
0707: && ((topStroke == null) || ((topColor == bottomColor)
0708: && (bottomColor == rightColor) && (rightColor == leftColor)))) {
0709: if (topStroke != null) {
0710: g.setColor(topColor);
0711: g2d.setStroke(topStroke);
0712:
0713: if (topWidth == 1) {
0714: //g2d.drawRect(x, y, width-2, height-2);
0715: g2d.drawRect(x, y, width - 1, height - 1);
0716: } else {
0717: int x1 = x + (leftWidth / 2);
0718:
0719: if ((leftWidth & 1) == 1) {
0720: x1--;
0721: }
0722:
0723: int x2 = (x + width) - 1 - (rightWidth / 2);
0724: int y1 = y + (topWidth / 2);
0725:
0726: if ((topWidth & 1) == 1) {
0727: y1--;
0728: }
0729:
0730: int y2 = (y + height) - 1 - (bottomWidth / 2);
0731: g2d.drawRect(x1, y1, x2 - x1, y2 - y1);
0732: }
0733: } else {
0734: // solid/inset/outset/ridge/groove/double
0735: g2d.setStroke(oldStroke);
0736:
0737: if ((topAltColor != null) && (topWidth > 1)) {
0738: g.setColor(topColor);
0739: paintTop(g2d, x, y, width, topWidth / 2,
0740: leftWidth / 2, rightWidth / 2);
0741: g.setColor(topAltColor);
0742: paintTop(g2d, x + (leftWidth / 2), y
0743: + (topWidth / 2), width - (leftWidth / 2)
0744: - (rightWidth / 2), (topWidth / 2)
0745: + (topWidth % 2), (leftWidth / 2)
0746: + (leftWidth % 2), (rightWidth / 2)
0747: + (rightWidth % 2));
0748: } else {
0749: g.setColor(topColor);
0750: paintTop(g2d, x, y, width, topWidth, leftWidth,
0751: rightWidth);
0752: }
0753:
0754: if ((bottomAltColor != null) && (bottomWidth > 1)) {
0755: g.setColor(bottomColor);
0756: paintBottom(g2d, x, y + height, width,
0757: bottomWidth / 2, leftWidth / 2,
0758: rightWidth / 2);
0759: g.setColor(bottomAltColor);
0760: paintBottom(g2d, x + (leftWidth / 2), (y + height)
0761: - (bottomWidth / 2), width
0762: - (leftWidth / 2) - (rightWidth / 2),
0763: (bottomWidth / 2) + (bottomWidth % 2),
0764: (leftWidth / 2) + (leftWidth % 2),
0765: (rightWidth / 2) + (rightWidth % 2));
0766: } else {
0767: g.setColor(bottomColor);
0768: paintBottom(g2d, x, y + height, width, bottomWidth,
0769: leftWidth, rightWidth);
0770: }
0771:
0772: g.setColor(leftColor);
0773:
0774: if ((leftAltColor != null) && (leftWidth > 1)) {
0775: g.setColor(leftColor);
0776: paintLeft(g2d, x, y, leftWidth / 2, height,
0777: topWidth / 2, bottomWidth / 2);
0778: g.setColor(leftAltColor);
0779: paintLeft(g2d, x + (leftWidth / 2)
0780: + (leftWidth % 2), y + (topWidth / 2)
0781: + (topWidth % 2), (leftWidth / 2)
0782: + (leftWidth % 2), height - (topWidth / 2)
0783: - (bottomWidth / 2), (topWidth / 2)
0784: + (topWidth % 2), (bottomWidth / 2)
0785: + (bottomWidth % 2));
0786: } else {
0787: g.setColor(leftColor);
0788: paintLeft(g2d, x, y, leftWidth, height, topWidth,
0789: bottomWidth);
0790: }
0791:
0792: if ((rightAltColor != null) && (rightWidth > 1)) {
0793: g.setColor(rightColor);
0794: paintRight(g2d, x + width, y, rightWidth / 2,
0795: height, topWidth / 2, bottomWidth / 2);
0796: g.setColor(rightAltColor);
0797: paintRight(g2d, (x + width) - (rightWidth / 2)
0798: + (rightWidth % 2), y + (topWidth / 2)
0799: + (topWidth % 2), (rightWidth / 2)
0800: + (rightWidth % 2), height - (topWidth / 2)
0801: - (bottomWidth / 2), (topWidth / 2)
0802: + (topWidth % 2), (bottomWidth / 2)
0803: + (bottomWidth % 2));
0804: } else {
0805: g.setColor(rightColor);
0806: paintRight(g2d, x + width, y, rightWidth, height,
0807: topWidth, bottomWidth);
0808: }
0809: }
0810: } else {
0811: if (topStroke != null) {
0812: g.setColor(topColor);
0813: g2d.setStroke(topStroke);
0814:
0815: int tx1 = x - leftWidth + (topWidth / 2);
0816:
0817: if ((topWidth & 1) == 1) {
0818: tx1--;
0819: }
0820:
0821: int ty = y + (topWidth / 2);
0822:
0823: if ((topWidth & 1) == 1) {
0824: ty--;
0825: }
0826:
0827: int tx2 = (x + width) - rightWidth - (topWidth / 2);
0828: g2d.drawLine(tx1, ty, tx2, ty);
0829: } else {
0830: // inset/outset/ridge/groove
0831: if (topAltColor != null) {
0832: // ridge or groove
0833: g2d.setStroke(oldStroke);
0834: g.setColor(topColor);
0835: paintTop(g2d, x, y, width, topWidth / 2,
0836: leftWidth / 2, rightWidth / 2);
0837: g.setColor(topAltColor);
0838: paintTop(g2d, x + (leftWidth / 2), y
0839: + (topWidth / 2), width - (leftWidth / 2)
0840: - (rightWidth / 2), (topWidth / 2)
0841: + (topWidth % 2), (leftWidth / 2)
0842: + (leftWidth % 2), (rightWidth / 2)
0843: + (rightWidth % 2));
0844: } else {
0845: g2d.setStroke(oldStroke);
0846: g.setColor(topColor);
0847: paintTop(g2d, x, y, width, topWidth, leftWidth,
0848: rightWidth);
0849: }
0850: }
0851:
0852: if (bottomStroke != null) {
0853: g.setColor(bottomColor);
0854: g2d.setStroke(bottomStroke);
0855:
0856: int tx1 = x - leftWidth + (bottomWidth / 2);
0857:
0858: if ((bottomWidth & 1) == 1) {
0859: tx1--;
0860: }
0861:
0862: int ty = (y + height) - (bottomWidth / 2);
0863: int tx2 = (x + width) - rightWidth - (bottomWidth / 2);
0864: g2d.drawLine(tx1, ty, tx2, ty);
0865: } else {
0866: // inset/outset/ridge/groove
0867: g.setColor(bottomColor);
0868: g2d.setStroke(oldStroke);
0869: paintBottom(g2d, x, y + height, width, bottomWidth,
0870: leftWidth, rightWidth);
0871: }
0872:
0873: if (leftStroke != null) {
0874: g.setColor(leftColor);
0875: g2d.setStroke(leftStroke);
0876:
0877: int tx = x - (leftWidth / 2);
0878: int ty1 = y - topWidth + (leftWidth / 2);
0879: int ty2 = (y + height + bottomWidth) - (leftWidth / 2);
0880:
0881: if ((leftWidth & 1) == 1) {
0882: tx--;
0883: }
0884:
0885: if ((leftWidth & 1) == 1) {
0886: ty1--;
0887: }
0888:
0889: g2d.drawLine(tx, ty1, tx, ty2);
0890: } else {
0891: // inset/outset/ridge/groove
0892: g.setColor(leftColor);
0893: g2d.setStroke(oldStroke);
0894: paintLeft(g2d, x, y, leftWidth, height, topWidth,
0895: bottomWidth);
0896: }
0897:
0898: if (rightStroke != null) {
0899: g.setColor(rightColor);
0900: g2d.setStroke(rightStroke);
0901:
0902: int tx = (x + width) - (rightWidth / 2);
0903: int ty1 = y - topWidth + (rightWidth / 2);
0904:
0905: if ((rightWidth & 1) == 0) {
0906: ty1--;
0907: }
0908:
0909: int ty2 = (y + height + bottomWidth) - (rightWidth / 2);
0910: g2d.drawLine(tx, ty1, tx, ty2);
0911: } else {
0912: // inset/outset/ridge/groove
0913: g.setColor(rightColor);
0914: g2d.setStroke(oldStroke);
0915: paintRight(g2d, x + width, y, rightWidth, height,
0916: topWidth, bottomWidth);
0917: }
0918: }
0919:
0920: g.setColor(oldColor);
0921: g2d.setStroke(oldStroke);
0922: }
0923:
0924: /**
0925: Paint this:
0926: <pre>
0927: (x,y) w
0928: ---------------------
0929: \ /
0930: \ / h
0931: ---------------
0932: (x2,y2) (x3,y2)
0933: </pre>
0934: x,y points to the top left corner. h is the height.
0935: left is x2-x. Right is w-x3.
0936: */
0937: private void paintTop(Graphics2D g2d, int x, int y, int w, int h,
0938: int left, int right) {
0939: if (h == 1) {
0940: g2d.drawLine(x, y, (x + w) - 1, y);
0941: } else {
0942: pointsX[0] = x;
0943: pointsY[0] = y;
0944: pointsX[1] = x + w;
0945: pointsY[1] = y;
0946: pointsX[2] = (x + w) - right;
0947: pointsY[2] = y + h;
0948: pointsX[3] = x + left;
0949: pointsY[3] = pointsY[2];
0950: g2d.fillPolygon(pointsX, pointsY, 4); // not including endpoint!
0951: }
0952: }
0953:
0954: /**
0955: Paint this:
0956: <pre>
0957: (x2,y2) (x3,y2)
0958: ---------------
0959: / \ h
0960: / \
0961: ---------------------
0962: (x,y) w
0963: </pre>
0964: x,y points to the bottom left corner. h is the height.
0965: left is x2-x. Right is x+w-x3.
0966: */
0967: private void paintBottom(Graphics2D g2d, int x, int y, int w,
0968: int h, int left, int right) {
0969: if (bottomWidth == 1) {
0970: g2d.drawLine(x, y - 1, (x + w) - 1, y - 1);
0971: } else {
0972: pointsX[0] = x;
0973: pointsY[0] = y;
0974: pointsX[1] = x + w;
0975: pointsY[1] = y;
0976: pointsX[2] = (x + w) - right;
0977: pointsY[2] = y - h;
0978: pointsX[3] = x + left;
0979: pointsY[3] = pointsY[2];
0980: g2d.fillPolygon(pointsX, pointsY, 4); // not including endpoint!
0981: }
0982: }
0983:
0984: /**
0985: Paint this:
0986: <pre>
0987: (x,y) |\
0988: | \
0989: | | (x2, y2)
0990: h | |
0991: | |
0992: | | (x2, y3)
0993: | /
0994: |/
0995: w
0996: </pre>
0997: x,y points to the top left corner. h is the height, w is the width.
0998: top is y2-y. Bottom is y+h-y3.
0999: */
1000: private void paintLeft(Graphics2D g2d, int x, int y, int w, int h,
1001: int top, int bottom) {
1002: if (leftWidth == 1) {
1003: g2d.drawLine(x, y, x, (y + h) - 1);
1004: } else {
1005: pointsX[0] = x;
1006: pointsY[0] = y;
1007: pointsX[1] = x;
1008: pointsY[1] = y + h;
1009: pointsX[2] = x + w;
1010: pointsY[2] = (y + h) - bottom;
1011: pointsX[3] = pointsX[2];
1012: pointsY[3] = y + top;
1013: g2d.fillPolygon(pointsX, pointsY, 4); // not including endpoint!
1014: }
1015: }
1016:
1017: /**
1018: Paint this:
1019: <pre>
1020: /| (x,y)
1021: / |
1022: (x2,y2) | |
1023: | | h
1024: | |
1025: (x2,y3) | |
1026: \ |
1027: \|
1028: w
1029: </pre>
1030: x,y points to the top right corner. h is the height, w is the width.
1031: top is y2-y. Bottom is y+h-y3.
1032: */
1033: private void paintRight(Graphics2D g2d, int x, int y, int w, int h,
1034: int top, int bottom) {
1035: if (rightWidth == 1) {
1036: g2d.drawLine(x - 1, y, x - 1, (y + h) - 1);
1037: } else {
1038: pointsX[0] = x;
1039: pointsY[0] = y;
1040: pointsX[1] = x;
1041: pointsY[1] = y + h;
1042: pointsX[2] = x - w;
1043: pointsY[2] = (y + h) - bottom;
1044: pointsX[3] = pointsX[2];
1045: pointsY[3] = y + top;
1046: g2d.fillPolygon(pointsX, pointsY, 4); // not including endpoint!
1047: }
1048: }
1049:
1050: /**
1051: * Returns the insets of the border.
1052: * @param c the component for which this border insets value applies
1053: */
1054: public Insets getBorderInsets(Component c) {
1055: return new Insets(1, 1, 1, 1);
1056: }
1057:
1058: /**
1059: * Returns whether or not the border is opaque. If the border
1060: * is opaque, it is responsible for filling in it's own
1061: * background when painting.
1062: */
1063: public boolean isBorderOpaque() {
1064: return false;
1065: }
1066:
1067: public int getLeftBorderWidth() {
1068: return leftWidth;
1069: }
1070:
1071: public int getRightBorderWidth() {
1072: return rightWidth;
1073: }
1074:
1075: public int getTopBorderWidth() {
1076: return topWidth;
1077: }
1078:
1079: public int getBottomBorderWidth() {
1080: return bottomWidth;
1081: }
1082:
1083: public String toString() {
1084: return super .toString() + "[" + leftWidth + "," + rightWidth
1085: + "," + topWidth + "," + bottomWidth + // NOI18N
1086: "," + leftColor + "]"; // NOI18N
1087: }
1088: }
|