1 /*
2 * Copyright (c) 2003-2008 by Cosylab d. d.
3 *
4 * This file is part of CosyBeans-Common.
5 *
6 * CosyBeans-Common is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * CosyBeans-Common is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with CosyBeans-Common. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 package com.cosylab.gui.components;
21
22 import com.cosylab.gui.components.util.FontHelper;
23 import com.cosylab.gui.components.util.PaintHelper;
24
25 import com.cosylab.logging.DebugLogger;
26
27 import java.awt.Dimension;
28 import java.awt.Font;
29 import java.awt.Graphics;
30 import java.awt.Graphics2D;
31 import java.awt.Insets;
32 import java.awt.event.ComponentAdapter;
33 import java.awt.event.ComponentEvent;
34 import java.awt.event.HierarchyEvent;
35 import java.awt.event.HierarchyListener;
36 import java.awt.event.KeyAdapter;
37 import java.awt.event.KeyEvent;
38
39 import java.util.logging.Level;
40 import java.util.logging.Logger;
41
42 import javax.swing.JTextField;
43 import javax.swing.text.Document;
44
45
46 /**
47 * Descedant of JTextField that implements font resizing. When in resize mode,
48 * the field automatically adjusts its text font size to fit best into the
49 * space available. By default resizing is off.
50 *
51 * @author <a href="mailto:jernej.kamenik@cosylab.com">Jernej Kamenik</a>
52 * @version $id$
53 *
54 * @see javax.swing.JTextField
55 */
56 public class ResizableTextField extends JTextField
57 {
58 private static final long serialVersionUID = 1L;
59 private boolean resizable;
60 private boolean enhanced;
61 private Dimension preferredSize;
62 private Dimension minimumSize;
63 private Font userFont;
64 private final Logger logger = DebugLogger.getLogger("RTF", Level.OFF);
65
66 /**
67 * Creates an empty resizable text field.
68 */
69 public ResizableTextField()
70 {
71 this(null, null, 0);
72 }
73
74 /**
75 * Creates a resizable text field with text.
76 *
77 * @param arg0 String
78 */
79 public ResizableTextField(String arg0)
80 {
81 this(null, arg0, 0);
82 }
83
84 /**
85 * Creates a resizable text field with specified number of columns.
86 *
87 * @param arg0 int
88 */
89 public ResizableTextField(int arg0)
90 {
91 this(null, null, arg0);
92 }
93
94 /**
95 * Creates a resizable text field with text and specified number of
96 * columns.
97 *
98 * @param arg0 document to be used for parsing strings.
99 * @param arg1 int
100 */
101 public ResizableTextField(String arg0, int arg1)
102 {
103 this(null, arg0, arg1);
104 }
105
106 /**
107 * Creates a resizable text field with document, text and specified number
108 * of columns.
109 *
110 * @param arg0 document to be used for parsing strings.
111 * @param arg1 text
112 * @param arg2 number of columns
113 */
114 public ResizableTextField(Document arg0, String arg1, int arg2)
115 {
116 super(arg0, arg1, arg2);
117 addComponentListener(new ResizableComponentAdapter());
118 addHierarchyListener(new ResizableComponentAdapter());
119 addKeyListener(new ResizableKeyAdapter());
120 setFont(FontHelper.getDefaultFont());
121 }
122
123 /**
124 * Returns the resizable setting.
125 *
126 * @return boolean
127 */
128 public boolean isResizable()
129 {
130 return resizable;
131 }
132
133 /**
134 * Sets whether the ResizableTextField should adjust its font size
135 * according to the size of the component.
136 *
137 * @param newResizable boolean
138 */
139 public void setResizable(boolean newResizable)
140 {
141 boolean oldResizable = resizable;
142
143 if (oldResizable == newResizable) {
144 return;
145 }
146
147 resizable = newResizable;
148 firePropertyChange("resizable", oldResizable, newResizable);
149
150 if (resizable) {
151 resize();
152 } else {
153 if (userFont != null) {
154 super.setFont(userFont);
155 } else {
156 super.setFont(FontHelper.getDefaultFont());
157 }
158
159 //doLayout();
160 //repaint();
161 }
162 }
163
164 /**
165 * Sets the enhanced mode setting. When true, the text field is painted
166 * with anti-aliasing rendering hints.
167 *
168 * @param newEnhanced
169 */
170 public void setEnhanced(boolean newEnhanced)
171 {
172 boolean oldEnhanced = enhanced;
173
174 if (oldEnhanced == newEnhanced) {
175 return;
176 }
177
178 enhanced = newEnhanced;
179 firePropertyChange("enhanced", oldEnhanced, newEnhanced);
180 repaint();
181 }
182
183 /**
184 * Returns the enhanced mode setting.
185 *
186 * @return boolean
187 */
188 public boolean isEnhanced()
189 {
190 return enhanced;
191 }
192
193 /**
194 * Bugfix for bug 4243496
195 *
196 * @param margin Insets
197 *
198 * @see javax.swing.text.JTextComponent#setMargin(java.awt.Insets)
199 */
200 public void setMargin(Insets margin)
201 {
202 if (margin == null) {
203 super.setMargin(new Insets(0, 2, 0, 2));
204
205 return;
206 }
207
208 if (margin.left == 0) {
209 super.setMargin(new Insets(margin.top, 2, margin.bottom,
210 margin.right));
211
212 return;
213 }
214
215 super.setMargin(margin);
216 }
217
218 /**
219 * This method was overloaded to implement font resizing.
220 *
221 * @param text String
222 *
223 * @see JTextField#setText(String)
224 */
225 public void setText(String text)
226 {
227 if (logger!=null) logger.info("text='" + text + "'");
228 super.setText(text);
229 //revalidate();
230
231 //resize();
232 }
233
234 /**
235 * Adjusts the size of the font to the size of the text field. It is safe
236 * to call this method without checking if field is in fact resizable.
237 */
238 protected void resize()
239 {
240 if (!isResizable()) {
241 return;
242 }
243
244 //Thread.dumpStack();
245 logger.fine("resizing");
246
247 /*int size = calculateFontSize(getSize(), getText());
248
249 if (size == getFont().getSize()) {
250 logger.fine("size not changed "+size);
251 return;
252 }
253
254 logger.fine("new size "+size);
255 super.setFont(FontHelper.getFontWithSize(size, getFont()));*/
256 Font f = FontHelper.calculateFittingFont(this, getFont(), getText(),
257 getColumns(), 0, Integer.MAX_VALUE);
258
259 if (f != getFont()) {
260 logger.fine("font resized to " + f.getSize() + " from "
261 + getFont().getSize());
262 super.setFont(f);
263 }
264 }
265
266 /*
267 * Calculates new size of font for textto matcch available space.
268 *
269 * @return int
270 */
271 /*protected int calculateFontSize(Dimension dim, String text)
272 {
273 // ideal size from vert evaluation
274 int vSize = Math.max((dim.height * 14) / 20,
275 FontHelper.getDefaultFontSize());
276
277 int l = (text != null)
278 ? getFontMetrics(FontHelper.getDefaultFont()).stringWidth(text)
279 : (getFontMetrics(FontHelper.getDefaultFont()).stringWidth("m") * getColumns());
280
281 // ideal size from vert evaluation
282 int hSize = (int)((dim.width * 0.9) / l * FontHelper.getDefaultFontSize());
283
284 int s = Math.min(vSize, hSize);
285 s = Math.max(s, 1);
286
287 return s;
288 }*/
289
290 /**
291 * This method was overloaded to implement font resizing.
292 *
293 * @return Dimension
294 *
295 * @see JTextField#getPreferredSize()
296 */
297 public Dimension getPreferredSize()
298 {
299 if (preferredSize != null) {
300 return preferredSize;
301 }
302
303 Font font = FontHelper.getDefaultFont();
304 int height = (font.getSize() * 20) / 13;
305 int width = (getColumns() * getFontMetrics(font).stringWidth("m") * 10) / 8;
306
307 return new Dimension(Math.max(width, 1), Math.max(height, 1));
308 }
309
310 /**
311 * This method was overloaded to implement font resizing.
312 *
313 * @see javax.swing.JComponent#setPreferredSize(Dimension)
314 */
315 public void setPreferredSize(Dimension newPreferredSize)
316 {
317 preferredSize = newPreferredSize;
318 super.setPreferredSize(newPreferredSize);
319 }
320
321 /**
322 * This method was overriden to implement font resizing.
323 *
324 * @see java.awt.Component#getMinimumSize()
325 */
326 public Dimension getMinimumSize()
327 {
328 if (minimumSize != null) {
329 return minimumSize;
330 }
331
332 Font font = FontHelper.getDefaultFont();
333 int height = (font.getSize() * 20) / 13;
334 int width = (getColumns() * getFontMetrics(font).stringWidth("m") * 10) / 8;
335
336 return new Dimension(Math.max(width, 1), Math.max(height, 1));
337 }
338
339 /**
340 * This method was overriden to implement font resizing.
341 *
342 * @see javax.swing.JComponent#setMinimumSize(Dimension)
343 */
344 public void setMinimumSize(Dimension newMinimumSize)
345 {
346 minimumSize = newMinimumSize;
347 super.setMinimumSize(newMinimumSize);
348 }
349
350 /**
351 * This method hes been overriden to implement the feature of enhanced
352 * anti-aliasing paint of the text field.
353 *
354 * @param g Graphics
355 *
356 * @see javax.swing.JComponent#paintComponent(Graphics)
357 */
358 protected void paintComponent(Graphics g)
359 {
360 //Thread.dumpStack();
361 logger.fine("painting");
362
363 if (enhanced) {
364 ((Graphics2D)g).addRenderingHints(PaintHelper.getAntialiasingHints());
365 }
366
367 super.paintComponent(g);
368 }
369
370 /**
371 * This method was overriden to support resizing and user supplied fonts.
372 * In resizable mode, this setting is ignored.
373 *
374 * @see java.awt.Component#setFont(java.awt.Font)
375 */
376 public void setFont(Font f)
377 {
378 userFont = f;
379 super.setFont(f);
380 }
381
382 /**
383 * Listens for ComponentEvents and notifies text resizing if enabled.
384 *
385 * @author <a href="mailto:jernej.kamenik@cosylab.com">Jernej Kamenik</a>
386 * @version $id$
387 */
388 protected class ResizableComponentAdapter extends ComponentAdapter implements HierarchyListener
389 {
390 /**
391 * Invokes text resizing if enabled when component is resized.
392 *
393 * @param e ComponentEvent
394 *
395 * @see java.awt.event.ComponentListener#componentResized(ComponentEvent)
396 */
397 public void componentResized(ComponentEvent e)
398 {
399 logger.finest("resize event " + e);
400 resize();
401 }
402
403 /**
404 * Invoke resizing of the font when the hiearachy changes, because this
405 * action might as well change the size of the components, but the resize
406 * is not fired, because the component did not have parent prior to that.
407 *
408 * @param e the event describing the hierarchy change
409 */
410 public void hierarchyChanged(HierarchyEvent e) {
411 resize();
412 }
413 }
414
415 /**
416 * Listens for KeyEvents and resizes the text if enabled.
417 *
418 * @author <a href="mailto:jernej.kamenik@cosylab.com">Jernej Kamenik</a>
419 * @version $id$
420 */
421 protected class ResizableKeyAdapter extends KeyAdapter
422 {
423 /**
424 * Invokes text resizing if enabled while text is typed.
425 *
426 * @param e
427 *
428 * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
429 */
430 public void keyReleased(KeyEvent e)
431 {
432 if (resizable) {
433 revalidate();
434 resize();
435 }
436 }
437 }
438 }
439
440 /* __oOo__ */