1 | // Copyright 2004-2007 Jean-Francois Poilpret |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | package net.sourceforge.hiveboard.util; |
16 | |
17 | import java.awt.Component; |
18 | import java.awt.Dimension; |
19 | import java.awt.EventQueue; |
20 | import java.awt.Font; |
21 | import java.awt.event.ActionEvent; |
22 | import java.awt.event.ActionListener; |
23 | |
24 | import javax.swing.Box; |
25 | import javax.swing.BoxLayout; |
26 | import javax.swing.JComboBox; |
27 | import javax.swing.JComponent; |
28 | import javax.swing.JList; |
29 | import javax.swing.JSpinner; |
30 | import javax.swing.JToggleButton; |
31 | import javax.swing.SpinnerNumberModel; |
32 | import javax.swing.event.ChangeEvent; |
33 | import javax.swing.event.ChangeListener; |
34 | |
35 | import org.jdesktop.layout.Baseline; |
36 | import org.jvnet.substance.SubstanceDefaultListCellRenderer; |
37 | |
38 | import net.sourceforge.hivegui.component.FontComboFactory; |
39 | |
40 | //CSOFF: ClassDataAbstractionCouplingCheck |
41 | public class FontChooserHelper |
42 | { |
43 | static final private String NAME = "font-chooser"; |
44 | |
45 | public FontChooserHelper() |
46 | { |
47 | this(NAME); |
48 | } |
49 | |
50 | public FontChooserHelper(String name) |
51 | { |
52 | // Initialize generic listener for almost all widgets |
53 | ActionListener listener = new ActionListener() |
54 | { |
55 | public void actionPerformed(ActionEvent e) |
56 | { |
57 | // Workaround for substance issue #283: |
58 | // EDT rule: one should not change a component property from |
59 | // one of this component's listeners. |
60 | updateFontDeferred(); |
61 | } |
62 | }; |
63 | |
64 | // Create widgets |
65 | _fontName = FontComboFactory.getFontCombo(); |
66 | _fontName.setName(name + "-face"); |
67 | _fontSize = new JSpinner( |
68 | new SpinnerNumberModel(DEFAULT_FONT_SIZE, 1, MAX_FONT_SIZE, 1)); |
69 | _fontSize.setName(name + "-size"); |
70 | initButton(_boldFont, listener); |
71 | _boldFont.setName(name + "-bold"); |
72 | initButton(_italicFont, listener); |
73 | _italicFont.setName(name + "-italic"); |
74 | initButton(_underlineFont, listener); |
75 | _underlineFont.setName(name + "-underline"); |
76 | // Note: the following instruction must not be moved before (it depends |
77 | // on all widgets being already instantiated) |
78 | _fontName.setRenderer(new SubstanceDefaultListCellRenderer() |
79 | { |
80 | @Override public Component getListCellRendererComponent( |
81 | JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) |
82 | { |
83 | super.getListCellRendererComponent( |
84 | list, value, index, isSelected, cellHasFocus); |
85 | Font font = FontChooserHelper.this.getFont( |
86 | (String) value, COMBO_FONT_DISPLAY_SIZE); |
87 | setFont(font); |
88 | return this; |
89 | } |
90 | }); |
91 | |
92 | // Compute the ideal size to use for the combo box, based on the |
93 | // length of the font name (not based on actual width of every single |
94 | // font face). That calculation is suboptimal in precision but is |
95 | // performant and easy to understand. |
96 | // However, we take some safety by multiplying the resulting width |
97 | // by a 1.5 factor. |
98 | int numFonts = _fontName.getModel().getSize(); |
99 | String longestFont = ""; |
100 | for (int i = 0; i < numFonts; i++) |
101 | { |
102 | String font = (String) _fontName.getModel().getElementAt(i); |
103 | if (font.length() > longestFont.length()) |
104 | { |
105 | longestFont = font; |
106 | } |
107 | } |
108 | _fontName.setPrototypeDisplayValue(longestFont); |
109 | |
110 | // Setup widgets sizes |
111 | Dimension size = new Dimension(_fontSize.getPreferredSize()); |
112 | size.width = (int) (_fontName.getPreferredSize().width * FONT_WIDTH_MULTIPLIER); |
113 | _fontName.setMinimumSize(size); |
114 | _fontName.setPreferredSize(size); |
115 | _fontName.setMaximumSize(size); |
116 | size = new Dimension(_fontSize.getPreferredSize()); |
117 | size.width = FONTSIZE_FIELD_WIDTH; |
118 | _fontSize.setMaximumSize(size); |
119 | |
120 | // Setup additional listeners |
121 | _fontName.addActionListener(listener); |
122 | _fontSize.addChangeListener(new ChangeListener() |
123 | { |
124 | public void stateChanged(ChangeEvent e) |
125 | { |
126 | // Workaround for substance issue #283: |
127 | // EDT rule: one should not change a component property from |
128 | // one of this component's listeners. |
129 | updateFontDeferred(); |
130 | } |
131 | }); |
132 | } |
133 | |
134 | static private void initButton(JToggleButton button, ActionListener listener) |
135 | { |
136 | button.addActionListener(listener); |
137 | button.setBorderPainted(true); |
138 | button.setRolloverEnabled(true); |
139 | } |
140 | |
141 | public JComponent getChooserPane() |
142 | { |
143 | if (_pane == null) |
144 | { |
145 | _pane = new FontBox(); |
146 | } |
147 | return _pane; |
148 | } |
149 | |
150 | public class FontBox extends JComponent |
151 | { |
152 | public FontBox() |
153 | { |
154 | setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); |
155 | add(_fontName); |
156 | add(Box.createHorizontalStrut(COMPONENTS_GAP)); |
157 | add(_fontSize); |
158 | add(Box.createHorizontalStrut(COMPONENTS_GAP)); |
159 | add(_boldFont); |
160 | add(_italicFont); |
161 | //TODO add(_underlineFont); |
162 | } |
163 | |
164 | public int getBaseline(int width, int height) |
165 | { |
166 | return Baseline.getBaseline(_fontSize) + _fontSize.getY(); |
167 | } |
168 | } |
169 | |
170 | public JComponent[] getComponents() |
171 | { |
172 | return new JComponent[] { _fontName, |
173 | (JComponent) Box.createHorizontalStrut(COMPONENTS_GAP), |
174 | _fontSize, |
175 | (JComponent) Box.createHorizontalStrut(COMPONENTS_GAP), |
176 | _boldFont, |
177 | _italicFont}; |
178 | //TODO _underlineFont}; |
179 | } |
180 | |
181 | // To be overridden if necessary |
182 | protected void updateFont(Font font) |
183 | { |
184 | } |
185 | |
186 | public Font getFont() |
187 | { |
188 | if (_font == null) |
189 | { |
190 | updateFont(); |
191 | } |
192 | return _font; |
193 | } |
194 | |
195 | public void setFont(Font font) |
196 | { |
197 | _font = font; |
198 | _fontName.setSelectedItem(font.getFamily()); |
199 | _fontSize.setValue(font.getSize()); |
200 | _boldFont.setSelected((font.getStyle() & Font.BOLD) != 0); |
201 | _italicFont.setSelected((font.getStyle() & Font.ITALIC) != 0); |
202 | //TODO _underlineFont.setSelected(); |
203 | } |
204 | |
205 | protected void updateFont() |
206 | { |
207 | _fontName.setFont( |
208 | getFont((String) _fontName.getSelectedItem(), COMBO_FONT_DISPLAY_SIZE)); |
209 | _font = getFont((String) _fontName.getSelectedItem(), (Integer) _fontSize.getValue()); |
210 | updateFont(_font); |
211 | } |
212 | |
213 | protected void updateFontDeferred() |
214 | { |
215 | EventQueue.invokeLater(new Runnable() |
216 | { |
217 | public void run() |
218 | { |
219 | updateFont(); |
220 | } |
221 | }); |
222 | } |
223 | |
224 | protected Font getFont(String face, int size) |
225 | { |
226 | int style = Font.PLAIN; |
227 | if (_boldFont.isSelected()) |
228 | { |
229 | style |= Font.BOLD; |
230 | } |
231 | if (_italicFont.isSelected()) |
232 | { |
233 | style |= Font.ITALIC; |
234 | } |
235 | //TODO if (_underlineFont.isSelected()) |
236 | // style |= Font.UNDERLINE; |
237 | Font font = new Font(face, style, size); |
238 | return font; |
239 | } |
240 | |
241 | static final private int DEFAULT_FONT_SIZE = 12; |
242 | static final private int COMBO_FONT_DISPLAY_SIZE = 14; |
243 | static final private int MAX_FONT_SIZE = 100; |
244 | static final private int FONTSIZE_FIELD_WIDTH = 50; |
245 | static final private int COMPONENTS_GAP = 3; |
246 | static final private double FONT_WIDTH_MULTIPLIER = 1.5; |
247 | |
248 | private final JComboBox _fontName; |
249 | private final JSpinner _fontSize; |
250 | private final JToggleButton _boldFont = new JToggleButton(); |
251 | private final JToggleButton _italicFont = new JToggleButton(); |
252 | private final JToggleButton _underlineFont = new JToggleButton(); |
253 | private JComponent _pane = null; |
254 | private Font _font = null; |
255 | } |
256 | //CSON: ClassDataAbstractionCouplingCheck |