View Javadoc

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__ */