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-2006 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: /*
042: * PlainAquaToolbarUI.java
043: *
044: * Created on January 17, 2004, 3:00 AM
045: */
046:
047: package org.netbeans.swing.plaf.aqua;
048:
049: import org.netbeans.swing.plaf.util.UIUtils;
050:
051: import javax.swing.*;
052: import javax.swing.BoxLayout;
053: import javax.swing.border.Border;
054: import javax.swing.plaf.ButtonUI;
055: import javax.swing.plaf.ComponentUI;
056: import javax.swing.plaf.basic.BasicToolBarUI;
057: import java.awt.*;
058: import java.awt.event.ContainerEvent;
059: import java.awt.event.ContainerListener;
060: import java.awt.geom.*;
061: import java.awt.image.BufferedImage;
062: import java.util.HashMap;
063: import java.util.Map;
064:
065: /** A ToolbarUI subclass that gets rid of all borders
066: * on buttons and provides a finder-style toolbar look.
067: *
068: * @author Tim Boudreau
069: */
070: public class PlainAquaToolbarUI extends BasicToolBarUI implements
071: ContainerListener {
072: private static final AquaTbBorder aquaborder = new AquaTbBorder();
073: // private static final AquaToolbarLayout layout = new AquaToolbarLayout();
074:
075: private static final Color UPPER_GRADIENT_TOP = new Color(255, 255,
076: 255);
077: private static final Color UPPER_GRADIENT_BOTTOM = new Color(228,
078: 230, 232);
079:
080: private static final Color LOWER_GRADIENT_TOP = new Color(228, 227,
081: 215);
082: private static final Color LOWER_GRADIENT_BOTTOM = new Color(249,
083: 249, 249);
084:
085: /** Creates a new instance of PlainAquaToolbarUI */
086: public PlainAquaToolbarUI() {
087: }
088:
089: public static ComponentUI createUI(JComponent c) {
090: return new PlainAquaToolbarUI();
091: }
092:
093: public void installUI(JComponent c) {
094: super .installUI(c);
095: //Editor will try install a custom border - just use ours
096: UIManager.put("Nb.Editor.Toolbar.border", aquaborder);
097: c.setBorder(aquaborder);
098: c.setOpaque(true);
099: c.addContainerListener(this );
100:
101: installButtonUIs(c);
102: }
103:
104: public void uninstallUI(JComponent c) {
105: super .uninstallUI(c);
106: c.setBorder(null);
107: c.removeContainerListener(this );
108: }
109:
110: /** Cache for images - normally only ever two - one for editor toolbar
111: * height and one for main window toolbar height
112: */
113: private static Map<Integer, BufferedImage> icache = new HashMap<Integer, BufferedImage>();
114:
115: private BufferedImage getCacheImage(JComponent c) {
116: BufferedImage img = icache.get(new Integer(c.getHeight()));
117: //Don't make a cache image for very small sizes - we're probably just
118: //initializing, and if not, painting will be cheap enough. Also
119: //ensure that the mid area width is at least a reasonable width for
120: //multiple blits on wide toolbars
121: if (img == null && c.getWidth() > (arcsize * 2) + 24
122: && c.getHeight() > 12) {
123: img = new BufferedImage(c.getWidth(), c.getHeight(),
124: BufferedImage.TYPE_INT_ARGB_PRE); //INT_ARGB_PRE is native raster format on mac os
125:
126: paintInto(img.getGraphics(), c);
127: icache.put(Integer.valueOf(c.getHeight()), img);
128: }
129: return img;
130: }
131:
132: public void update(Graphics g, JComponent c) {
133: paint(g, c);
134: }
135:
136: public void paint(Graphics g, JComponent c) {
137: if (!c.isOpaque() || c.getWidth() < 4 || c.getHeight() < 4) {
138: return;
139: }
140: BufferedImage img = getCacheImage(c);
141: if (img == null) {
142: paintInto(g, c);
143: } else {
144: //Use a cached image for painting - the geometry ops and
145: //plethora of GradientPaints in aqua toolbars are too expensive
146: //for use on every paint - slows down everything and allocates
147: //an 6 width * height integer rasters for per toolbar per paint
148: paintImage((Graphics2D) g, img, c);
149: }
150: }
151:
152: /**
153: * Paint a cached image of the toolbar. Paints the left edge and
154: * content, if necessary, repeats the interior section until the
155: * width less-the-edges is full, then paints the end cap.
156: */
157: private void paintImage(Graphics2D g2d, BufferedImage img,
158: JComponent c) {
159: int w = c.getWidth();
160: int h = c.getHeight();
161: int imgw = img.getWidth();
162: AffineTransform nullTransform = AffineTransform
163: .getTranslateInstance(0, 0);
164: if (w == imgw) {
165: //Perfect fit, we're done
166: g2d.drawRenderedImage(img, nullTransform);
167: } else if (w > imgw) {
168: //Wider than the image - loop and blit the interior, then draw the
169: //end cap
170: g2d.drawRenderedImage(img, nullTransform);
171: int uncovered = w - (imgw - (arcsize * 2));
172: int x = imgw - arcsize;
173: //Get the mid section of the cached image, less the end caps
174: do {
175: BufferedImage mid = img.getSubimage(arcsize, 0, Math
176: .min(uncovered, imgw - (arcsize * 2)), h);
177: //blit it until we've filled all the space we need to
178: g2d.drawRenderedImage(mid, AffineTransform
179: .getTranslateInstance(x, 0));
180: x += mid.getWidth();
181: uncovered -= mid.getWidth();
182: } while (x < w - arcsize);
183: BufferedImage rightEdge = img.getSubimage(imgw - arcsize,
184: 0, arcsize, h);
185: g2d.drawRenderedImage(rightEdge, AffineTransform
186: .getTranslateInstance(w - arcsize, 0));
187: } else if (w < imgw) {
188: //Narrower than the image - draw the width we need, then the end cap
189: BufferedImage left = img.getSubimage(0, 0, imgw - arcsize,
190: h);
191: //Don't blit the full image, or we will get white "ears" on the right side
192: g2d.drawRenderedImage(left, nullTransform);
193: BufferedImage right = img.getSubimage(imgw - arcsize, 0,
194: arcsize, h);
195: g2d.drawRenderedImage(right, AffineTransform
196: .getTranslateInstance(w - arcsize, 0));
197: }
198: }
199:
200: private void paintInto(Graphics g, JComponent c) {
201: UIUtils.configureRenderingHints(g);
202: Color temp = g.getColor();
203: Dimension size = c.getSize();
204:
205: Shape s = aquaborder.getInteriorShape(size.width, size.height);
206: Shape clip = g.getClip();
207: if (clip != null) {
208: Area a = new Area(clip);
209: a.intersect(new Area(s));
210: g.setClip(a);
211: } else {
212: g.setClip(s);
213: }
214:
215: Graphics2D g2d = (Graphics2D) g;
216: //g.setColor (Color.ORANGE);
217:
218: g2d.setPaint(aquaborder.getUpperPaint(size.width, size.height));
219: g2d
220: .fill(aquaborder.getUpperBevelShape(size.width,
221: size.height));
222: g2d.setPaint(aquaborder.getLowerPaint(size.width, size.height));
223: g2d
224: .fill(aquaborder.getLowerBevelShape(size.width,
225: size.height));
226:
227: g.setClip(clip);
228: g.setColor(temp);
229: }
230:
231: protected Border createRolloverBorder() {
232: return BorderFactory.createEmptyBorder(2, 2, 2, 2);
233: }
234:
235: protected Border createNonRolloverBorder() {
236: return createRolloverBorder();
237: }
238:
239: private Border createNonRolloverToggleBorder() {
240: return createRolloverBorder();
241: }
242:
243: protected void setBorderToRollover(Component c) {
244: if (c instanceof AbstractButton) {
245: ((AbstractButton) c).setBorderPainted(false);
246: ((AbstractButton) c).setBorder(BorderFactory
247: .createEmptyBorder());
248: // ((AbstractButton) c).setContentAreaFilled(false);
249: ((AbstractButton) c).setOpaque(false);
250: }
251: if (c instanceof JComponent) {
252: ((JComponent) c).setOpaque(false);
253: }
254: }
255:
256: protected void setBorderToNormal(Component c) {
257: if (c instanceof AbstractButton) {
258: ((AbstractButton) c).setBorderPainted(false);
259: // ((AbstractButton) c).setContentAreaFilled(false);
260: ((AbstractButton) c).setOpaque(false);
261: }
262: if (c instanceof JComponent) {
263: ((JComponent) c).setOpaque(false);
264: }
265: }
266:
267: public void setFloating(boolean b, Point p) {
268: //nobody wants this
269: }
270:
271: private void installButtonUI(Component c) {
272: if (c instanceof AbstractButton) {
273: ((AbstractButton) c).setUI(buttonui);
274: }
275: if (c instanceof JComponent) {
276: ((JComponent) c).setOpaque(false);
277: }
278: }
279:
280: private void installButtonUIs(Container parent) {
281: Component[] c = parent.getComponents();
282: for (int i = 0; i < c.length; i++) {
283: installButtonUI(c[i]);
284: }
285: }
286:
287: private static final ButtonUI buttonui = new AquaToolBarButtonUI();
288:
289: public void componentAdded(ContainerEvent e) {
290: installButtonUI(e.getChild());
291: Container c = (Container) e.getSource();
292: if ("editorToolbar".equals(c.getName())) {
293: //It's an editor toolbar. Aqua's combo box ui paints outside
294: //of its literal component bounds, and doesn't honor opacity.
295: //Need to ensure the toolbar is tall enough that its border is
296: //not hidden.
297: Dimension min = new Dimension(32, 34);
298: ((JComponent) e.getContainer()).setPreferredSize(min);
299: }
300: }
301:
302: public void componentRemoved(ContainerEvent e) {
303: //do nothing
304: }
305:
306: private static final boolean isFinderLook(Component c) {
307: return Boolean.getBoolean("apple.awt.brushMetalLook");
308: }
309:
310: static int arcsize = 13;
311:
312: static class AquaTbBorder implements Border {
313:
314: public Insets getBorderInsets(Component c) {
315: return new Insets(2, 4, 0, 0);
316: }
317:
318: public boolean isBorderOpaque() {
319: return false;
320: }
321:
322: public void paintBorder(Component c, Graphics g, int x, int y,
323: int w, int h) {
324: UIUtils.configureRenderingHints(g);
325:
326: boolean finderLook = isFinderLook(c);
327: Color col;
328: if (finderLook) {
329: col = mezi(UIManager.getColor("controlShadow"),
330: UIManager.getColor("control")); //NOI18N
331: } else {
332: col = UIManager.getColor("controlShadow");
333: }
334:
335: g.setColor(col);
336:
337: int ytop = y;
338:
339: drawUpper(g, x, y, ytop, w, h);
340:
341: g.setColor(mezi(col, UIManager.getColor("control"))); //NOI18N
342: if (finderLook) {
343: drawUpper(g, x + 1, y, ytop + 1, w - 2, h - 1);
344: }
345:
346: if (finderLook) {
347: col = mezi(UIManager.getColor("controlShadow"),
348: UIManager.getColor("control")); //NOI18N
349: drawLower(g, x, y, w, h, col, finderLook);
350: } else {
351: // drawLower (g, x, y, w, h, Color.LIGHT_GRAY, finderLook);
352: drawLower(g, x, y - 1, w, h, col, finderLook);
353: g.setColor(new Color(200, 200, 200));
354: g.drawLine(x + (arcsize / 2) - 3, y + h - 1, x + w
355: - (arcsize / 2), y + h - 1);
356: }
357: }
358:
359: private void drawLower(Graphics g, int x, int y, int w, int h,
360: Color col, boolean finderLook) {
361:
362: g.setColor(col);
363: g.drawLine(x, y + (arcsize / 2), x, y + h - (arcsize / 2));
364: g.drawLine(x + w - 1, y + (arcsize / 2), x + w - 1, y + h
365: - (arcsize / 2));
366:
367: if (!finderLook) {
368: g.setColor(new Color(220, 220, 220));
369: g.drawArc(x - 1, y + 1 + h - arcsize, arcsize, arcsize,
370: 180, 90);
371: g.drawArc((x + 1) + w - (arcsize + 1), y + 1 + h
372: - (arcsize + 1), arcsize, arcsize, 270, 90);
373: g.setColor(col);
374: }
375:
376: g.drawArc(x, y + h - arcsize, arcsize, arcsize, 180, 90);
377: g.drawArc(x + w - (arcsize + 1), y + h - (arcsize + 1),
378: arcsize, arcsize, 270, 90);
379:
380: if (!finderLook) {
381:
382: g.setColor(new Color(80, 80, 80));
383: }
384: g.drawLine(x + (arcsize / 2) - 3, y + h - 1, x + w
385: - (arcsize / 2), y + h - 1);
386: }
387:
388: private void drawUpper(Graphics g, int x, int y, int ytop,
389: int w, int h) {
390: g.drawArc(x, ytop, arcsize, arcsize, 90, 90);
391:
392: g.drawArc(x + w - (arcsize + 1), ytop, arcsize, arcsize,
393: 90, -90);
394:
395: g.drawLine(x + (arcsize / 2), ytop, x + w - (arcsize / 2),
396: ytop);
397: }
398:
399: Paint getUpperPaint(Color top, Color bottom, int w, int h) {
400: GradientPaint result = UIUtils.getGradientPaint(0, h / 4,
401: top, 0, (h / 2) + (h / 4), bottom, false);
402: return result;
403: }
404:
405: Paint getLowerPaint(Color top, Color bottom, int w, int h) {
406: GradientPaint result = UIUtils.getGradientPaint(0, h / 2,
407: top, 0, (h / 2) + (h / 4), bottom, false);
408:
409: return result;
410: }
411:
412: Paint getUpperPaint(int w, int h) {
413: return getUpperPaint(UPPER_GRADIENT_TOP,
414: UPPER_GRADIENT_BOTTOM, w, h);
415: }
416:
417: Paint getLowerPaint(int w, int h) {
418: return getLowerPaint(LOWER_GRADIENT_TOP,
419: LOWER_GRADIENT_BOTTOM, w, h);
420: }
421:
422: Shape getInteriorShape(int w, int h) {
423: RoundRectangle2D r2d = new RoundRectangle2D.Double(0, 0, w,
424: h, arcsize, arcsize);
425: return r2d;
426: }
427:
428: Shape getUpperBevelShape(int w, int h) {
429: int[] xpoints = new int[] { 0, 0, h / 2, w - (h / 4), w, w,
430: 0 };
431:
432: int[] ypoints = new int[] { 0, h - (h / 4), h / 2, h / 2,
433: h / 4, 0, 0 };
434: Polygon p = new Polygon(xpoints, ypoints, ypoints.length);
435: return p;
436: }
437:
438: Shape getLowerBevelShape(int w, int h) {
439: int[] xpoints = new int[] { 0, 0, h / 4, w - (h / 4), w, w,
440: 0 };
441:
442: int[] ypoints = new int[] { h, h - (h / 4), h / 2, h / 2,
443: h / 4, h, h
444:
445: };
446: Polygon p = new Polygon(xpoints, ypoints, ypoints.length);
447: return p;
448: }
449: }
450:
451: private static Color mezi(Color c1, Color c2) {
452: return new Color((c1.getRed() + c2.getRed()) / 2, (c1
453: .getGreen() + c2.getGreen()) / 2, (c1.getBlue() + c2
454: .getBlue()) / 2);
455: }
456:
457: }
|