001: /*
002: * Copyright (c) 2001-2007 JGoodies Karsten Lentzsch. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of JGoodies Karsten Lentzsch nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package com.jgoodies.looks.windows;
032:
033: import java.awt.Color;
034: import java.awt.Component;
035: import java.awt.Graphics;
036: import java.awt.Insets;
037:
038: import javax.swing.AbstractButton;
039: import javax.swing.ButtonModel;
040: import javax.swing.JButton;
041: import javax.swing.JToggleButton;
042: import javax.swing.UIDefaults;
043: import javax.swing.UIManager;
044: import javax.swing.border.AbstractBorder;
045: import javax.swing.border.Border;
046: import javax.swing.border.CompoundBorder;
047: import javax.swing.border.EmptyBorder;
048: import javax.swing.plaf.BorderUIResource;
049: import javax.swing.plaf.UIResource;
050: import javax.swing.plaf.basic.BasicBorders;
051: import javax.swing.plaf.basic.BasicGraphicsUtils;
052:
053: /**
054: * Consists of static inner classes that define different
055: * <code>Borders</code> used in the JGoodies Windows look&feel.
056: *
057: * @author Karsten Lentzsch
058: * @version $Revision: 1.5 $
059: */
060: final class WindowsBorders {
061:
062: private WindowsBorders() {
063: // Overrides default constructor; prevents instantiation.
064: }
065:
066: // Accessing and Creating Borders ***************************************************
067:
068: private static Border menuBorder;
069: private static Border xpMenuBorder;
070: private static Border menuItemBorder;
071: private static Border popupMenuBorder;
072: private static Border noMarginPopupMenuBorder;
073: private static Border separatorBorder;
074: private static Border etchedBorder;
075: private static Border menuBarHeaderBorder;
076: private static Border toolBarHeaderBorder;
077: private static Border rolloverButtonBorder;
078:
079: /**
080: * Returns a <code>Border</code> for a <code>JButton</code>.
081: */
082: public static Border getButtonBorder() {
083: UIDefaults table = UIManager.getLookAndFeelDefaults();
084: Border outerBorder = new ButtonBorder(table
085: .getColor("Button.shadow"), table
086: .getColor("Button.darkShadow"), table
087: .getColor("Button.light"), table
088: .getColor("Button.highlight"), table
089: .getColor("controlText"));
090:
091: Border buttonBorder = new BorderUIResource.CompoundBorderUIResource(
092: outerBorder, new BasicBorders.MarginBorder());
093: return buttonBorder;
094: }
095:
096: /**
097: * Returns a Border for a JMenu in classic mode.
098: */
099: static Border getMenuBorder() {
100: if (menuBorder == null) {
101: menuBorder = new BorderUIResource.CompoundBorderUIResource(
102: new MenuBorder(), new BasicBorders.MarginBorder());
103: }
104: return menuBorder;
105: }
106:
107: /**
108: * Returns a Border for a JMenu in XP mode.
109: */
110: static Border getXPMenuBorder() {
111: if (xpMenuBorder == null) {
112: xpMenuBorder = new BasicBorders.MarginBorder();
113: }
114: return xpMenuBorder;
115: }
116:
117: /**
118: * Returns a border instance for a <code>JMenuItem</code>.
119: */
120: static Border getMenuItemBorder() {
121: if (menuItemBorder == null) {
122: menuItemBorder = new BorderUIResource(
123: new BasicBorders.MarginBorder());
124: }
125: return menuItemBorder;
126: }
127:
128: /**
129: * Returns a separator border instance for <code>JMenuBar</code> or <code>JToolBar</code>.
130: */
131: static Border getSeparatorBorder() {
132: if (separatorBorder == null) {
133: separatorBorder = new BorderUIResource.CompoundBorderUIResource(
134: new SeparatorBorder(),
135: new BasicBorders.MarginBorder());
136: }
137: return separatorBorder;
138: }
139:
140: /**
141: * Returns an etched border instance for <code>JMenuBar</code> or <code>JToolBar</code>.
142: */
143: static Border getEtchedBorder() {
144: if (etchedBorder == null) {
145: etchedBorder = new BorderUIResource.CompoundBorderUIResource(
146: new EtchedBorder(), new BasicBorders.MarginBorder());
147: }
148: return etchedBorder;
149: }
150:
151: /**
152: * Returns a special border for a <code>JMenuBar</code> that
153: * is used in a header just above a <code>JToolBar</code>.
154: */
155: static Border getMenuBarHeaderBorder() {
156: if (menuBarHeaderBorder == null) {
157: menuBarHeaderBorder = new BorderUIResource.CompoundBorderUIResource(
158: new MenuBarHeaderBorder(),
159: new BasicBorders.MarginBorder());
160: }
161: return menuBarHeaderBorder;
162: }
163:
164: /**
165: * Returns a border instance for a <code>JPopupMenu</code>.
166: *
167: * @return the lazily created popup menu border
168: */
169: static Border getPopupMenuBorder() {
170: if (popupMenuBorder == null) {
171: popupMenuBorder = new PopupMenuBorder();
172: }
173: return popupMenuBorder;
174: }
175:
176: /**
177: * Returns a no-margin border instance for a <code>JPopupMenu</code>.
178: *
179: * @return the lazily created no-margin popup menu border
180: */
181: static Border getNoMarginPopupMenuBorder() {
182: if (noMarginPopupMenuBorder == null) {
183: noMarginPopupMenuBorder = new NoMarginPopupMenuBorder();
184: }
185: return noMarginPopupMenuBorder;
186: }
187:
188: /**
189: * Returns a special border for a <code>JToolBar</code> that
190: * is used in a header just below a <code>JMenuBar</code>.
191: */
192: static Border getToolBarHeaderBorder() {
193: if (toolBarHeaderBorder == null) {
194: toolBarHeaderBorder = new BorderUIResource.CompoundBorderUIResource(
195: new ToolBarHeaderBorder(),
196: new BasicBorders.MarginBorder());
197: }
198: return toolBarHeaderBorder;
199: }
200:
201: /**
202: * Returns a border for a rollover <code>AbstractButton</code>.
203: */
204: static Border getRolloverButtonBorder() {
205: if (rolloverButtonBorder == null) {
206: rolloverButtonBorder = new CompoundBorder(
207: // No UIResource
208: new RolloverButtonBorder(),
209: new RolloverMarginBorder());
210: }
211: return rolloverButtonBorder;
212: }
213:
214: // Helper Classes *******************************************************************
215:
216: // Copied from BasicBorders, has correct black color for the outer default rectangle.
217: private static final class ButtonBorder extends AbstractBorder
218: implements UIResource {
219:
220: private static final Insets EMPTY_INSETS = new Insets(0, 0, 0,
221: 0);
222:
223: private final Color shadow;
224: private final Color darkShadow;
225: private final Color highlight;
226: private final Color lightHighlight;
227: private final Color defaultColor;
228:
229: public ButtonBorder(Color shadow, Color darkShadow,
230: Color highlight, Color lightHighlight,
231: Color defaultColor) {
232: this .shadow = shadow;
233: this .darkShadow = darkShadow;
234: this .highlight = highlight;
235: this .lightHighlight = lightHighlight;
236: this .defaultColor = defaultColor;
237: }
238:
239: public void paintBorder(Component c, Graphics g, int x, int y,
240: int width, int height) {
241: boolean isPressed = false;
242: boolean isDefault = false;
243:
244: if (c instanceof AbstractButton) {
245: AbstractButton b = (AbstractButton) c;
246: ButtonModel model = b.getModel();
247:
248: isPressed = model.isPressed() && model.isArmed();
249: if (c instanceof JButton) {
250: isDefault = ((JButton) c).isDefaultButton();
251: }
252: }
253: drawBezel(g, x, y, width, height, isPressed, isDefault,
254: shadow, darkShadow, highlight, lightHighlight,
255: defaultColor);
256: }
257:
258: public Insets getBorderInsets(Component c) {
259: return getBorderInsets(c, EMPTY_INSETS);
260: }
261:
262: public Insets getBorderInsets(Component c, Insets insets) {
263: // leave room for default visual
264: insets.top = 2;
265: insets.left = insets.bottom = insets.right = 3;
266: return insets;
267: }
268:
269: }
270:
271: /**
272: * An abstract superclass for borders.
273: */
274: private abstract static class AbstractButtonBorder extends
275: AbstractBorder implements UIResource {
276:
277: private static final Insets INSETS = new Insets(2, 2, 2, 2);
278:
279: public void paintBorder(Component c, Graphics g, int x, int y,
280: int w, int h) {
281: AbstractButton button = (AbstractButton) c;
282: ButtonModel model = button.getModel();
283:
284: //
285: //System.out.println("Pressed=" + model.isPressed() + "; armed=" + model.isArmed());
286: //if (!model.isArmed()) return;
287:
288: if (model.isPressed())
289: WindowsUtils.drawPressed3DBorder(g, x, y, w, h);
290: else
291: WindowsUtils.drawFlush3DBorder(g, x, y, w, h);
292: }
293:
294: public Insets getBorderInsets(Component c) {
295: return INSETS;
296: }
297: }
298:
299: /**
300: * A border used for <code>Buttons</code> that have the rollover property enabled.
301: */
302: private static final class RolloverButtonBorder extends
303: AbstractButtonBorder {
304:
305: public void paintBorder(Component c, Graphics g, int x, int y,
306: int w, int h) {
307: AbstractButton b = (AbstractButton) c;
308: ButtonModel model = b.getModel();
309:
310: if (!model.isEnabled())
311: return;
312:
313: if (!(c instanceof JToggleButton)) {
314: if (model.isRollover()) // && !( model.isPressed() && !model.isArmed()))
315: super .paintBorder(c, g, x, y, w, h);
316: return;
317: }
318:
319: if (model.isSelected())
320: WindowsUtils.drawPressed3DBorder(g, x, y, w, h);
321: else if (model.isRollover()) {
322: super .paintBorder(c, g, x, y, w, h);
323: /*
324: if (model.isPressed() && model.isArmed()) {
325: ExtMetalUtils.drawPressed3DBorder(g, x, y, w, h);
326: } else {
327: ExtMetalUtils.drawFlush3DBorder(g, x, y, w, h);
328: }*/
329: }
330: }
331: }
332:
333: /**
334: * A border which is like a Margin border but it will only honor the margin
335: * if the margin has been explicitly set by the developer.
336: */
337: private static final class RolloverMarginBorder extends EmptyBorder {
338:
339: private RolloverMarginBorder() {
340: super (1, 1, 1, 1);
341: }
342:
343: public Insets getBorderInsets(Component c) {
344: return getBorderInsets(c, new Insets(0, 0, 0, 0));
345: }
346:
347: public Insets getBorderInsets(Component c, Insets insets) {
348: Insets margin = null;
349:
350: if (c instanceof AbstractButton) {
351: margin = ((AbstractButton) c).getMargin();
352: }
353: if (margin == null || margin instanceof UIResource) {
354: // default margin so replace
355: insets.left = left;
356: insets.top = top;
357: insets.right = right;
358: insets.bottom = bottom;
359: } else {
360: // Margin which has been explicitly set by the user.
361: insets.left = margin.left;
362: insets.top = margin.top;
363: insets.right = margin.right;
364: insets.bottom = margin.bottom;
365: }
366: return insets;
367: }
368: }
369:
370: /**
371: * A border that looks like a separator line; used for menu bars and tool bars.
372: */
373: private static final class SeparatorBorder extends AbstractBorder
374: implements UIResource {
375:
376: private static final Insets INSETS = new Insets(0, 3, 2, 1);
377:
378: public void paintBorder(Component c, Graphics g, int x, int y,
379: int w, int h) {
380: g.translate(x, y);
381: g.setColor(UIManager.getColor("Separator.foreground"));
382: g.drawLine(0, h - 2, w - 1, h - 2);
383:
384: g.setColor(UIManager.getColor("Separator.background"));
385: g.drawLine(0, h - 1, w - 1, h - 1);
386: g.translate(-x, -y);
387: }
388:
389: public Insets getBorderInsets(Component c) {
390: return INSETS;
391: }
392: }
393:
394: /**
395: * A thin raised border.
396: */
397: static final class ThinRaisedBorder extends AbstractBorder
398: implements UIResource {
399:
400: private static final Insets INSETS = new Insets(1, 1, 1, 1);
401:
402: public void paintBorder(Component c, Graphics g, int x, int y,
403: int w, int h) {
404: WindowsUtils.drawFlush3DBorder(g, x, y, w, h);
405: }
406:
407: public Insets getBorderInsets(Component c) {
408: return INSETS;
409: }
410: }
411:
412: /**
413: * A thin lowered border.
414: */
415: static final class ThinLoweredBorder extends AbstractBorder
416: implements UIResource {
417:
418: private static final Insets INSETS = new Insets(1, 1, 1, 1);
419:
420: public void paintBorder(Component c, Graphics g, int x, int y,
421: int w, int h) {
422: WindowsUtils.drawPressed3DBorder(g, x, y, w, h);
423: }
424:
425: public Insets getBorderInsets(Component c) {
426: return INSETS;
427: }
428: }
429:
430: /**
431: * A border used for menu bars and tool bars in <code>HeaderStyle.SINGLE</code>.
432: * The bar is wrapped by an inner thin raised border,
433: * which in turn is wrapped by an outer thin lowered border.
434: */
435: private static final class EtchedBorder extends AbstractBorder
436: implements UIResource {
437:
438: private static final Insets INSETS = new Insets(2, 2, 2, 2);
439:
440: public void paintBorder(Component c, Graphics g, int x, int y,
441: int w, int h) {
442: WindowsUtils.drawPressed3DBorder(g, x, y, w, h);
443: WindowsUtils.drawFlush3DBorder(g, x + 1, y + 1, w - 2,
444: h - 2);
445: }
446:
447: public Insets getBorderInsets(Component c) {
448: return INSETS;
449: }
450: }
451:
452: /**
453: * A border used for menu bars in <code>HeaderStyle.BOTH</code>.
454: * The menu bar and tool bar are wrapped by a thin raised border,
455: * both together are wrapped by a thin lowered border.
456: */
457: private static final class MenuBarHeaderBorder extends
458: AbstractBorder implements UIResource {
459:
460: private static final Insets INSETS = new Insets(2, 2, 1, 2);
461:
462: public void paintBorder(Component c, Graphics g, int x, int y,
463: int w, int h) {
464: WindowsUtils.drawPressed3DBorder(g, x, y, w, h + 1);
465: WindowsUtils.drawFlush3DBorder(g, x + 1, y + 1, w - 2,
466: h - 1);
467: }
468:
469: public Insets getBorderInsets(Component c) {
470: return INSETS;
471: }
472: }
473:
474: private static final class PopupMenuBorder extends AbstractBorder
475: implements UIResource {
476:
477: private static final Insets INSETS = new Insets(3, 3, 3, 3);
478:
479: public void paintBorder(Component c, Graphics g, int x, int y,
480: int w, int h) {
481: g.translate(x, y);
482: g.setColor(UIManager.getColor("controlShadow"));
483: g.drawRect(0, 0, w - 1, h - 1);
484: g.setColor(UIManager.getColor("MenuItem.background"));
485: g.drawRect(1, 1, w - 3, h - 3);
486: g.drawRect(2, 2, w - 5, h - 5);
487: g.translate(-x, -y);
488: }
489:
490: public Insets getBorderInsets(Component c) {
491: return INSETS;
492: }
493: }
494:
495: private static final class NoMarginPopupMenuBorder extends
496: AbstractBorder implements UIResource {
497:
498: private static final Insets INSETS = new Insets(1, 1, 1, 1);
499:
500: public void paintBorder(Component c, Graphics g, int x, int y,
501: int w, int h) {
502: g.translate(x, y);
503: g.setColor(UIManager.getColor("controlShadow"));
504: g.drawRect(0, 0, w - 1, h - 1);
505: // g.setColor(UIManager.getColor("MenuItem.background"));
506: // g.drawRect(1, 1, 0, h-3);
507: g.translate(-x, -y);
508: }
509:
510: public Insets getBorderInsets(Component c) {
511: return INSETS;
512: }
513: }
514:
515: /**
516: * A border used for tool bars in <code>HeaderStyle.BOTH</code>.
517: * The menu bar and tool bar are wrapped by a thin raised border,
518: * both together are wrapped by a thin lowered border.
519: */
520: private static final class ToolBarHeaderBorder extends
521: AbstractBorder implements UIResource {
522:
523: private static final Insets INSETS = new Insets(1, 2, 2, 2);
524:
525: public void paintBorder(Component c, Graphics g, int x, int y,
526: int w, int h) {
527: WindowsUtils.drawPressed3DBorder(g, x, y - 1, w, h + 1);
528: WindowsUtils.drawFlush3DBorder(g, x + 1, y, w - 2, h - 1);
529: }
530:
531: public Insets getBorderInsets(Component c) {
532: return INSETS;
533: }
534: }
535:
536: /**
537: * A border used for menus.
538: */
539: private static final class MenuBorder extends AbstractBorder
540: implements UIResource {
541:
542: private static final Insets INSETS = new Insets(1, 1, 1, 1);
543:
544: public void paintBorder(Component c, Graphics g, int x, int y,
545: int w, int h) {
546: AbstractButton b = (AbstractButton) c;
547: ButtonModel model = b.getModel();
548:
549: //System.out.println("rollover=" + model.isRollover());
550: //if ((3 < 4) || model.isRollover()) { // && !(model.isPressed() && !model.isArmed())) {
551: if (model.isSelected())
552: WindowsUtils.drawPressed3DBorder(g, x, y, w, h);
553: else if (model.isRollover())
554: WindowsUtils.drawFlush3DBorder(g, x, y, w, h);
555: //}
556: }
557:
558: public Insets getBorderInsets(Component c) {
559: return INSETS;
560: }
561:
562: }
563:
564: // Helper Code **********************************************************************
565:
566: // Copied from BasicGraphicsUtils, has an additional color for the default rectangle.
567: private static void drawBezel(Graphics g, int x, int y, int w,
568: int h, boolean isPressed, boolean isDefault, Color shadow,
569: Color darkShadow, Color highlight, Color lightHighlight,
570: Color defaultColor) {
571: Color oldColor = g.getColor(); // Make no net change to g
572: g.translate(x, y);
573:
574: if (isPressed && isDefault) {
575: g.setColor(darkShadow);
576: g.drawRect(0, 0, w - 1, h - 1);
577: g.setColor(shadow);
578: g.drawRect(1, 1, w - 3, h - 3);
579: } else if (isPressed) {
580: BasicGraphicsUtils.drawLoweredBezel(g, x, y, w, h, shadow,
581: darkShadow, highlight, lightHighlight);
582: } else if (isDefault) {
583: g.setColor(defaultColor);
584: g.drawRect(0, 0, w - 1, h - 1);
585:
586: g.setColor(lightHighlight);
587: g.drawLine(1, 1, 1, h - 3);
588: g.drawLine(2, 1, w - 3, 1);
589:
590: g.setColor(highlight);
591: g.drawLine(2, 2, 2, h - 4);
592: g.drawLine(3, 2, w - 4, 2);
593:
594: g.setColor(shadow);
595: g.drawLine(2, h - 3, w - 3, h - 3);
596: g.drawLine(w - 3, 2, w - 3, h - 4);
597:
598: g.setColor(darkShadow);
599: g.drawLine(1, h - 2, w - 2, h - 2);
600: g.drawLine(w - 2, h - 2, w - 2, 1);
601: } else {
602: g.setColor(lightHighlight);
603: g.drawLine(0, 0, 0, h - 1);
604: g.drawLine(1, 0, w - 2, 0);
605:
606: g.setColor(highlight);
607: g.drawLine(1, 1, 1, h - 3);
608: g.drawLine(2, 1, w - 3, 1);
609:
610: g.setColor(shadow);
611: g.drawLine(1, h - 2, w - 2, h - 2);
612: g.drawLine(w - 2, 1, w - 2, h - 3);
613:
614: g.setColor(darkShadow);
615: g.drawLine(0, h - 1, w - 1, h - 1);
616: g.drawLine(w - 1, h - 1, w - 1, 0);
617: }
618: g.translate(-x, -y);
619: g.setColor(oldColor);
620: }
621:
622: }
|