1 /*
2  * Copyright (C) 2005-2006 Sun Microsystems, Inc. All rights reserved. Use is
3  * subject to license terms.
4  */
5 
6 package org.jdesktop.layout;
7 
8 import java.awt.Container;
9 import javax.swing.ButtonModel;
10 import javax.swing.DefaultButtonModel;
11 import javax.swing.JComponent;
12 import javax.swing.JToggleButton;
13 import javax.swing.SwingConstants;
14 import javax.swing.plaf.metal.MetalLookAndFeel;
15 import javax.swing.plaf.metal.MetalTheme;
16 import java.lang.reflect.*;
17 
18 /**
19  * An implementation of <code>LayoutStyle</code> for the java look and feel.
20  * This information comes from the
21  * <a href="http://java.sun.com/products/jlf/ed2/book/HIG.Visual2.html">
22  * The Java Look and Feel Design Guidelines</a>.
23  *
24  * @version $Revision: 1.7 $
25  */
26 class MetalLayoutStyle extends LayoutStyle {
27     /**
28      * Whether or not we're using ocean, the default metal theme in 1.5.
29      */
30     private boolean isOcean;
31 
MetalLayoutStyle()32     public MetalLayoutStyle() {
33         isOcean = false;
34         try {
35             Method method = MetalLookAndFeel.class.
36                 getMethod("getCurrentTheme", (Class[])null);
37             isOcean = ((MetalTheme)method.invoke(null, (Object[])null)).
38                       getName() == "Ocean";
39         } catch (NoSuchMethodException nsme) {
40         } catch (IllegalAccessException iae) {
41         } catch (IllegalArgumentException iae2) {
42         } catch (InvocationTargetException ite) {
43         }
44     }
45 
46     // NOTE: The JLF makes reference to a number of guidelines in terms of
47     // 6 pixels - 1 pixel.  The rationale is because steel buttons have
48     // a heavy border followed by a light border, and so that if you pad
49     // by 6 pixels it'll look like 7.  Using 5 pixels than produces an effect
50     // of 6 pixels.  With Ocean things are different, the only component
51     // that you want this behavior to happen with is checkboxs.
52 
getPreferredGap(JComponent source, JComponent target, int type, int position, Container parent)53     public int getPreferredGap(JComponent source, JComponent target,
54                           int type, int position, Container parent) {
55         // Invoke super to check arguments.
56         super.getPreferredGap(source, target, type, position, parent);
57 
58         if (type == INDENT) {
59             if (position == SwingConstants.EAST || position == SwingConstants.WEST) {
60                 int gap = getButtonChildIndent(source, position);
61                 if (gap != 0) {
62                     return gap;
63                 }
64                 return 12;
65             }
66             // Treat vertical INDENT as RELATED
67             type = RELATED;
68         }
69 
70         String sourceCID = source.getUIClassID();
71         String targetCID = target.getUIClassID();
72         int offset;
73 
74         if (type == RELATED) {
75             if (sourceCID == "ToggleButtonUI" &&
76                       targetCID == "ToggleButtonUI") {
77                 ButtonModel sourceModel = ((JToggleButton)source).getModel();
78                 ButtonModel targetModel = ((JToggleButton)target).getModel();
79                 if ((sourceModel instanceof DefaultButtonModel) &&
80                     (targetModel instanceof DefaultButtonModel) &&
81                     (((DefaultButtonModel)sourceModel).getGroup() ==
82                      ((DefaultButtonModel)targetModel).getGroup()) &&
83                         ((DefaultButtonModel)sourceModel).getGroup() != null) {
84                     // When toggle buttons are exclusive (that is, they form a
85                     // radio button set), separate them with 2 pixels. This
86                     // rule applies whether the toggle buttons appear in a
87                     // toolbar or elsewhere in the interface.
88                     // Note: this number does not appear to include any borders
89                     // and so is not adjusted by the border of the toggle
90                     // button
91                     return 2;
92                 }
93                 // When toggle buttons are independent (like checkboxes)
94                 // and used outside a toolbar, separate them with 5
95                 // pixels.
96                 if (isOcean) {
97                     return 6;
98                 }
99                 return 5;
100             }
101             offset = 6;
102         }
103         else {
104             offset = 12;
105         }
106         if ((position == SwingConstants.EAST ||
107              position == SwingConstants.WEST) &&
108             ((sourceCID == "LabelUI" && targetCID != "LabelUI") ||
109              (sourceCID != "LabelUI" && targetCID == "LabelUI"))) {
110             // Insert 12 pixels between the trailing edge of a
111             // label and any associated components. Insert 12
112             // pixels between the trailing edge of a label and the
113             // component it describes when labels are
114             // right-aligned. When labels are left-aligned, insert
115             // 12 pixels between the trailing edge of the longest
116             // label and its associated component
117             return getCBRBPadding(source, target, position, offset + 6);
118         }
119         return getCBRBPadding(source, target, position, offset);
120     }
121 
getCBRBPadding(JComponent source, JComponent target, int position, int offset)122     int getCBRBPadding(JComponent source, JComponent target, int position,
123                        int offset) {
124         offset = super.getCBRBPadding(source, target, position, offset);
125         if (offset > 0) {
126             int buttonAdjustment = getButtonAdjustment(source, position);
127             if (buttonAdjustment == 0) {
128                 buttonAdjustment = getButtonAdjustment(target,
129                                                        flipDirection(position));
130             }
131             offset -= buttonAdjustment;
132         }
133         if (offset < 0) {
134             return 0;
135         }
136         return offset;
137     }
138 
getButtonAdjustment(JComponent source, int edge)139     private int getButtonAdjustment(JComponent source, int edge) {
140         String uid = source.getUIClassID();
141         if (uid == "ButtonUI" || uid == "ToggleButtonUI") {
142             if (!isOcean && (edge == SwingConstants.EAST ||
143                              edge == SwingConstants.SOUTH)) {
144                 return 1;
145             }
146         }
147         else if (edge == SwingConstants.SOUTH) {
148             if (uid == "RadioButtonUI" || (!isOcean && uid == "CheckBoxUI")) {
149                 return 1;
150             }
151         }
152         return 0;
153     }
154 
getContainerGap(JComponent component, int position, Container parent)155     public int getContainerGap(JComponent component, int position,
156             Container parent) {
157         super.getContainerGap(component, position, parent);
158         // Here's the rules we should be honoring:
159         //
160         // Include 11 pixels between the bottom and right
161         // borders of a dialog box and its command
162         // buttons. (To the eye, the 11-pixel spacing appears
163         // to be 12 pixels because the white borders on the
164         // lower and right edges of the button components are
165         // not visually significant.)
166         // NOTE: this last text was designed with Steel in mind, not Ocean.
167         //
168         // Insert 12 pixels between the edges of the panel and the
169         // titled border. Insert 11 pixels between the top of the
170         // title and the component above the titled border. Insert 12
171         // pixels between the bottom of the title and the top of the
172         // first label in the panel. Insert 11 pixels between
173         // component groups and between the bottom of the last
174         // component and the lower border.
175         return getCBRBPadding(component, position, 12 -
176                 getButtonAdjustment(component, position));
177     }
178 }
179