001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.visualweb.css2;
042:
043: import java.awt.Graphics;
044: import java.awt.Rectangle;
045: import javax.swing.ImageIcon;
046:
047: import org.netbeans.modules.visualweb.api.designer.cssengine.CssListValue;
048: import org.netbeans.modules.visualweb.api.designer.cssengine.CssProvider;
049: import org.netbeans.modules.visualweb.api.designer.cssengine.CssValue;
050: import org.netbeans.modules.visualweb.designer.CssUtilities;
051: import org.openide.ErrorManager;
052: import org.w3c.dom.Element;
053:
054: /**
055: * Paints the background image. Most of this code was extracted from
056: * Swing text's Css.java class. <p>
057: * @todo Update the code to use REPEAT, REPEAT_X etc. flags instead
058: * of constants
059: */
060: public class BackgroundImagePainter {
061: public static final short NO_REPEAT = 0;
062: public static final short REPEAT_X = 1;
063: public static final short REPEAT_Y = 2;
064: public static final short REPEAT = REPEAT_X | REPEAT_Y;
065: private static final short HORIZONTAL_RELATIVE = 4;
066: private static final short VERTICAL_RELATIVE = 8;
067: ImageIcon backgroundImage;
068: float hPosition;
069: float vPosition;
070:
071: // bit mask: 0 for repeat x, 1 for repeat y, 2 for horiz relative,
072: // 3 for vert relative
073: int flags;
074:
075: // These are used when painting, updatePaintCoordinates updates them.
076: private int paintX;
077: private int paintY;
078: private int paintMaxX;
079: private int paintMaxY;
080:
081: /**
082: * Construct a new background painter with the given repeat and position
083: */
084: // public BackgroundImagePainter(ImageIcon bgImage, Value repeatValue, ListValue positionValue) {
085: public BackgroundImagePainter(ImageIcon bgImage,
086: CssValue cssRepeatValue, CssListValue cssPositionValue,
087: Element element, int defaultFontSize) {
088: this .backgroundImage = bgImage;
089:
090: int repeat = BackgroundImagePainter.REPEAT;
091:
092: // if (repeatValue == CssValueConstants.REPEAT_VALUE) {
093: if (CssProvider.getValueService().isRepeatValue(cssRepeatValue)) {
094: repeat = BackgroundImagePainter.REPEAT;
095: // } else if (repeatValue == CssValueConstants.NO_REPEAT_VALUE) {
096: } else if (CssProvider.getValueService().isNoRepeatValue(
097: cssRepeatValue)) {
098: repeat = BackgroundImagePainter.NO_REPEAT;
099: // } else if (repeatValue == CssValueConstants.REPEAT_X_VALUE) {
100: } else if (CssProvider.getValueService().isRepeatXValue(
101: cssRepeatValue)) {
102: repeat = BackgroundImagePainter.REPEAT_X;
103: // } else if (repeatValue == CssValueConstants.REPEAT_Y_VALUE) {
104: } else if (CssProvider.getValueService().isRepeatYValue(
105: cssRepeatValue)) {
106: repeat = BackgroundImagePainter.REPEAT_Y;
107: }
108:
109: // XXX TODO - support background-attachment
110:
111: /*
112: * If fractionX is greater than or
113: * equal to zero, the horizontal position will be taken to be a
114: * fraction of the padding rectangle width, and fractionX is that fraction.
115: * In that case, left is ignored. Otherwise, left is the offset from
116: * the top left padding rectangle corner where the image top left corner
117: * will be located.
118: * Similarly for the vertical dimension.
119: */
120: int left = 0;
121: int top = 0;
122: float fractionX = 0.0f;
123: float fractionY = 0.0f;
124:
125: // if (positionValue != null) {
126: if (cssPositionValue != null) {
127: // assert positionValue.getLength() == 2;
128: if (cssPositionValue.getLength() != 2) {
129: ErrorManager.getDefault()
130: .notify(
131: ErrorManager.INFORMATIONAL,
132: new IllegalStateException(
133: "The lenght of the list value should be 2, but is not"
134: + "\nlistValue="
135: + cssPositionValue
136: + "\nlength="
137: + cssPositionValue
138: .getLength()));
139: } else {
140:
141: // Value horiz = positionValue.item(0);
142: // Value vert = positionValue.item(1);
143: CssValue cssHoriz = cssPositionValue.item(0);
144: CssValue cssVert = cssPositionValue.item(1);
145:
146: if ((cssHoriz != null) && (cssVert != null)) {
147: // if (horiz.getPrimitiveType() == CSSPrimitiveValue.CSS_PERCENTAGE) {
148: if (CssProvider.getValueService()
149: .isOfPrimitivePercentageType(cssHoriz)) {
150: fractionX = cssHoriz.getFloatValue() / 100.0f;
151: } else if (CssProvider.getValueService()
152: .isOfPrimitiveEmsType(cssHoriz)) {
153: int fontSize = CssUtilities
154: .getDesignerFontForElement(element,
155: null, defaultFontSize)
156: .getSize();
157: left = (int) (cssHoriz.getFloatValue() * fontSize);
158: // XXX we should allow negative percentages too!
159: fractionX = -2.0f; // cause BackgroundImagePainter to ignore it
160: } else {
161: left = (int) cssHoriz.getFloatValue();
162: // XXX we should allow negative percentages too!
163: fractionX = -2.0f; // cause BackgroundImagePainter to ignore it
164: }
165:
166: // if (vert.getPrimitiveType() == CSSPrimitiveValue.CSS_PERCENTAGE) {
167: if (CssProvider.getValueService()
168: .isOfPrimitivePercentageType(cssVert)) {
169: fractionY = cssVert.getFloatValue() / 100.0f;
170: } else if (CssProvider.getValueService()
171: .isOfPrimitiveEmsType(cssVert)) {
172: int fontSize = CssUtilities
173: .getDesignerFontForElement(element,
174: null, defaultFontSize)
175: .getSize();
176: top = (int) (cssVert.getFloatValue() * fontSize);
177: fractionY = -2.0f; // cause BackgroundImagePainter to ignore it
178: } else {
179: top = (int) cssVert.getFloatValue();
180: fractionY = -2.0f; // cause BackgroundImagePainter to ignore it
181: }
182: }
183:
184: }
185: }
186:
187: flags = repeat;
188:
189: if (fractionX >= -1.0) {
190: flags |= HORIZONTAL_RELATIVE;
191: hPosition = fractionX;
192: // assert left == 0;
193: if (left != 0) {
194: ErrorManager.getDefault().notify(
195: ErrorManager.INFORMATIONAL,
196: new IllegalStateException(
197: "left is expected to be 0, left="
198: + left)); // NOI18N
199: }
200: } else {
201: hPosition = left;
202: }
203:
204: if (fractionY >= -1.0) {
205: flags |= VERTICAL_RELATIVE;
206: vPosition = fractionY;
207: // assert top == 0;
208: if (top != 0) {
209: ErrorManager.getDefault().notify(
210: ErrorManager.INFORMATIONAL,
211: new IllegalStateException(
212: "top is expected to be 0, top=" + top)); // NOI18N
213: }
214: } else {
215: vPosition = top;
216: }
217:
218: // TODO - the old code premultiplied the font size here - WHY OH WHY?
219: // Ah, they thought the percentages were in terms of em's ?
220: }
221:
222: // /**
223: // * Returns the ImageIcon to draw in the background for
224: // * <code>attr</code>.
225: // */
226: // public static ImageIcon getBackgroundImage(WebForm webform, Element element) {
227: //// Value value = CssLookup.getValue(element, XhtmlCss.BACKGROUND_IMAGE_INDEX);
228: // CssValue cssValue = CssProvider.getEngineService().getComputedValueForElement(element, XhtmlCss.BACKGROUND_IMAGE_INDEX);
229: //
230: //// if (value == CssValueConstants.NONE_VALUE) {
231: // if (CssProvider.getValueService().isNoneValue(cssValue)) {
232: // return null;
233: // }
234: //
235: //// String urlString = value.getStringValue();
236: // String urlString = cssValue.getStringValue();
237: //
238: // // XXX This is wrong. I should get the -stylesheet- URL.
239: // // And what about linked style sheets?
240: // URL reference = webform.getMarkup().getBase();
241: // URL url = null;
242: //
243: // try {
244: // url = new URL(reference, urlString);
245: // } catch (MalformedURLException e) {
246: // e.printStackTrace();
247: //
248: // return null;
249: // }
250: //
251: // ImageCache cache = webform.getDocument().getImageCache();
252: // ImageIcon ii = cache.get(url);
253: //
254: // if (ii == null) {
255: // ii = new ImageIcon(url);
256: // cache.put(url, ii);
257: // }
258: //
259: // return ii;
260: // }
261:
262: // /**
263: // * Returns the ImageIcon to draw in the background for
264: // * <code>attr</code>.
265: // */
266: // public static ImageIcon getBackgroundImage(URL reference, Element element) {
267: //// Value value = CssLookup.getValue(element, XhtmlCss.BACKGROUND_IMAGE_INDEX);
268: // CssValue cssValue = CssProvider.getEngineService().getComputedValueForElement(element, XhtmlCss.BACKGROUND_IMAGE_INDEX);
269: //
270: //
271: //// if (value == CssValueConstants.NONE_VALUE) {
272: // if (CssProvider.getValueService().isNoneValue(cssValue)) {
273: // return null;
274: // }
275: //
276: //// String urlString = value.getStringValue();
277: // String urlString = cssValue.getStringValue();
278: //
279: // URL url = null;
280: //
281: // try {
282: // url = new URL(reference, urlString);
283: // } catch (MalformedURLException e) {
284: // e.printStackTrace();
285: //
286: // return null;
287: // }
288: //
289: // // ImageCache cache = doc.getImageCache();
290: // // ImageIcon ii = cache.get(url);
291: // // if (ii == null) {
292: // ImageIcon ii = new ImageIcon(url);
293: //
294: // // cache.put(url, ii);
295: // // }
296: // return ii;
297: // }
298:
299: public void paint(Graphics g, float x, float y, float w, float h) {
300: Rectangle clip = g.getClipBounds();
301:
302: if (clip != null) {
303: // Constrain the clip so that images don't draw outside the
304: // legal bounds.
305: g.clipRect((int) x, (int) y, (int) w, (int) h);
306: }
307:
308: if ((flags & REPEAT) == 0) {
309: // no repeating
310: int width = backgroundImage.getIconWidth();
311: int height = backgroundImage.getIconWidth();
312:
313: if ((flags & HORIZONTAL_RELATIVE) == HORIZONTAL_RELATIVE) {
314: paintX = (int) ((x + (w * hPosition)) - ((float) width * hPosition));
315: } else {
316: paintX = (int) x + (int) hPosition;
317: }
318:
319: if ((flags & VERTICAL_RELATIVE) == VERTICAL_RELATIVE) {
320: paintY = (int) ((y + (h * vPosition)) - ((float) height * vPosition));
321: } else {
322: paintY = (int) y + (int) vPosition;
323: }
324:
325: if ((clip == null)
326: || !(((paintX + width) <= clip.x)
327: || ((paintY + height) <= clip.y)
328: || (paintX >= (clip.x + clip.width)) || (paintY >= (clip.y + clip.height)))) {
329: backgroundImage.paintIcon(null, g, paintX, paintY);
330: }
331: } else {
332: int width = backgroundImage.getIconWidth();
333: int height = backgroundImage.getIconHeight();
334:
335: if ((width > 0) && (height > 0)) {
336: paintX = (int) x;
337: paintY = (int) y;
338: paintMaxX = (int) (x + w);
339: paintMaxY = (int) (y + h);
340:
341: // We also need to compute the position to begin painting at
342: // even when repeating (Swing's StyleSheet.BackgroundImagePainter
343: // wasn't doing this.)
344: int offsetX = 0;
345:
346: if ((flags & HORIZONTAL_RELATIVE) == HORIZONTAL_RELATIVE) {
347: offsetX = (int) ((w * hPosition) - ((float) width * hPosition));
348: } else {
349: offsetX = (int) hPosition;
350: }
351:
352: if ((flags & REPEAT_X) == REPEAT_X) {
353: if (offsetX > 0) {
354: // Move to a multiple before X
355: int mod = offsetX % width;
356:
357: if (mod > 0) {
358: mod -= width;
359: }
360:
361: offsetX = mod;
362: }
363: }
364:
365: paintX += offsetX;
366:
367: int offsetY = 0;
368:
369: if ((flags & VERTICAL_RELATIVE) == VERTICAL_RELATIVE) {
370: offsetY = (int) ((h * vPosition) - ((float) height * vPosition));
371: } else {
372: offsetY = (int) vPosition;
373: }
374:
375: if ((flags & REPEAT_Y) == REPEAT_Y) {
376: if (offsetY > 0) {
377: // Move to a multiple above y
378: int mod = offsetY % height;
379:
380: if (mod > 0) {
381: mod -= height;
382: }
383:
384: offsetY = mod;
385: }
386: }
387:
388: paintY += offsetY;
389:
390: if (updatePaintCoordinates(clip, width, height)) {
391: while (paintX < paintMaxX) {
392: int ySpot = paintY;
393:
394: while (ySpot < paintMaxY) {
395: backgroundImage.paintIcon(null, g, paintX,
396: ySpot);
397: ySpot += height;
398: }
399:
400: paintX += width;
401: }
402: }
403: }
404: }
405:
406: if (clip != null) {
407: // Reset clip.
408: g.setClip(clip.x, clip.y, clip.width, clip.height);
409: }
410: }
411:
412: private boolean updatePaintCoordinates(Rectangle clip, int width,
413: int height) {
414: if ((flags & REPEAT) == REPEAT_X) {
415: paintMaxY = paintY + 1;
416: } else if ((flags & REPEAT) == REPEAT_Y) {
417: paintMaxX = paintX + 1;
418: }
419:
420: if (clip != null) {
421: if (((flags & REPEAT) == REPEAT_X)
422: && (((paintY + height) <= clip.y) || (paintY > (clip.y + clip.height)))) {
423: // not visible.
424: return false;
425: }
426:
427: if (((flags & REPEAT) == REPEAT_Y)
428: && (((paintX + width) <= clip.x) || (paintX > (clip.x + clip.width)))) {
429: // not visible.
430: return false;
431: }
432:
433: if ((flags & REPEAT_X) == REPEAT_X) {
434: if ((clip.x + clip.width) < paintMaxX) {
435: if ((((clip.x + clip.width) - paintX) % width) == 0) {
436: paintMaxX = clip.x + clip.width;
437: } else {
438: paintMaxX = (((((clip.x + clip.width) - paintX) / width) + 1) * width)
439: + paintX;
440: }
441: }
442:
443: if (clip.x > paintX) {
444: paintX = ((clip.x - paintX) / width * width)
445: + paintX;
446: }
447: }
448:
449: if ((flags & REPEAT_Y) == REPEAT_Y) {
450: if ((clip.y + clip.height) < paintMaxY) {
451: if ((((clip.y + clip.height) - paintY) % height) == 0) {
452: paintMaxY = clip.y + clip.height;
453: } else {
454: paintMaxY = (((((clip.y + clip.height) - paintY) / height) + 1) * height)
455: + paintY;
456: }
457: }
458:
459: if (clip.y > paintY) {
460: paintY = ((clip.y - paintY) / height * height)
461: + paintY;
462: }
463: }
464: }
465:
466: // Valid
467: return true;
468: }
469: }
|