Source Code Cross Referenced for DefaultUnitConverter.java in  » Swing-Library » jgoodies-forms » com » jgoodies » forms » util » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Swing Library » jgoodies forms » com.jgoodies.forms.util 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * Copyright (c) 2002-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.forms.util;
032:
033:        import java.awt.Component;
034:        import java.awt.Font;
035:        import java.awt.FontMetrics;
036:        import java.beans.PropertyChangeEvent;
037:        import java.beans.PropertyChangeListener;
038:        import java.beans.PropertyChangeSupport;
039:        import java.util.HashMap;
040:        import java.util.Map;
041:        import java.util.logging.Logger;
042:
043:        import javax.swing.JButton;
044:        import javax.swing.JPanel;
045:        import javax.swing.UIManager;
046:
047:        /**
048:         * This is the default implementation of the {@link UnitConverter} interface.
049:         * It converts horizontal and vertical dialog base units to pixels.<p>
050:         *  
051:         * The horizontal base unit is equal to the average width, in pixels,
052:         * of the characters in the system font; the vertical base unit is equal
053:         * to the height, in pixels, of the font.
054:         * Each horizontal base unit is equal to 4 horizontal dialog units;
055:         * each vertical base unit is equal to 8 vertical dialog units.<p>
056:         * 
057:         * The DefaultUnitConverter computes dialog base units using a default font 
058:         * and a test string for the average character width. You can configure
059:         * the font and the test string via the bound Bean properties
060:         * <em>defaultDialogFont</em> and <em>averageCharacterWidthTestString</em>.
061:         * See also Microsoft's suggestion for a custom computation 
062:         * <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwue/html/ch14e.asp">here</a>.<p>
063:         * 
064:         * Since the Forms 1.1 this converter logs font information at
065:         * the <code>CONFIG</code> level.
066:         * 
067:         * @version $Revision: 1.5 $
068:         * @author  Karsten Lentzsch
069:         * @see     UnitConverter
070:         * @see     com.jgoodies.forms.layout.Size
071:         * @see     com.jgoodies.forms.layout.Sizes
072:         */
073:        public final class DefaultUnitConverter extends AbstractUnitConverter {
074:
075:            //    public static final String UPPERCASE_ALPHABET =
076:            //        "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
077:            //
078:            //    public static final String LOWERCASE_ALPHABET =
079:            //        "abcdefghijklmnopqrstuvwxyz";
080:
081:            private final static Logger LOGGER = Logger
082:                    .getLogger(DefaultUnitConverter.class.getName());
083:
084:            /**
085:             * Holds the sole instance that will be lazily instantiated.
086:             */
087:            private static DefaultUnitConverter instance;
088:
089:            /**
090:             * Holds the string that is used to compute the average character width.
091:             * By default this is just &quot;X&quot;.
092:             */
093:            private String averageCharWidthTestString = "X";
094:
095:            /**
096:             * Holds a custom font that is used to compute the global dialog base units.
097:             * If not set, a fallback font is is lazily created in method 
098:             * #getCachedDefaultDialogFont, which in turn looks up a font 
099:             * in method #lookupDefaultDialogFont.
100:             */
101:            private Font defaultDialogFont;
102:
103:            /**
104:             * If any <code>PropertyChangeListeners</code> have been registered,
105:             * the <code>changeSupport</code> field describes them.
106:             *
107:             * @serial
108:             * @see #addPropertyChangeListener(PropertyChangeListener)
109:             * @see #addPropertyChangeListener(String, PropertyChangeListener)
110:             * @see #removePropertyChangeListener(PropertyChangeListener)
111:             * @see #removePropertyChangeListener(String, PropertyChangeListener)
112:             */
113:            private PropertyChangeSupport changeSupport;
114:
115:            // Cached *****************************************************************
116:
117:            /**
118:             * Holds the cached global dialog base units that are used if 
119:             * a component is not (yet) available - for example in a Border.
120:             */
121:            private DialogBaseUnits cachedGlobalDialogBaseUnits = computeGlobalDialogBaseUnits();
122:
123:            /**
124:             * Maps <code>FontMetrics</code> to horizontal dialog base units.
125:             * This is a second-level cache, that stores dialog base units
126:             * for a <code>FontMetrics</code> object.
127:             */
128:            private Map cachedDialogBaseUnits = new HashMap();
129:
130:            /**
131:             * Holds a cached default dialog font that is used as fallback,
132:             * if no default dialog font has been set.
133:             * 
134:             * @see #getDefaultDialogFont()
135:             * @see #setDefaultDialogFont(Font)
136:             */
137:            private Font cachedDefaultDialogFont = null;
138:
139:            // Instance Creation and Access *******************************************
140:
141:            /**
142:             * Constructs a DefaultUnitConverter and registers
143:             * a listener that handles changes in the look&amp;feel.
144:             */
145:            private DefaultUnitConverter() {
146:                UIManager
147:                        .addPropertyChangeListener(new LookAndFeelChangeHandler());
148:                changeSupport = new PropertyChangeSupport(this );
149:            }
150:
151:            /**
152:             * Lazily instantiates and returns the sole instance.
153:             * 
154:             * @return the lazily instantiated sole instance 
155:             */
156:            public static DefaultUnitConverter getInstance() {
157:                if (instance == null) {
158:                    instance = new DefaultUnitConverter();
159:                }
160:                return instance;
161:            }
162:
163:            // Access to Bound Properties *********************************************
164:
165:            /**
166:             * Returns the string used to compute the average character width.
167:             * By default it is initialized to &quot;X&quot;.
168:             * 
169:             * @return the test string used to compute the average character width
170:             */
171:            public String getAverageCharacterWidthTestString() {
172:                return averageCharWidthTestString;
173:            }
174:
175:            /**
176:             * Sets a string that will be used to compute the average character width.
177:             * By default it is initialized to &quot;X&quot;. You can provide
178:             * other test strings, for example: 
179:             * <ul>
180:             *  <li>&quot;Xximeee&quot;</li>
181:             *  <li>&quot;ABCEDEFHIJKLMNOPQRSTUVWXYZ&quot;</li> 
182:             *  <li>&quot;abcdefghijklmnopqrstuvwxyz&quot;</li>
183:             * </ul>
184:             * 
185:             * @param newTestString   the test string to be used
186:             * @throws IllegalArgumentException if the test string is empty
187:             * @throws NullPointerException     if the test string is <code>null</code>
188:             */
189:            public void setAverageCharacterWidthTestString(String newTestString) {
190:                if (newTestString == null)
191:                    throw new NullPointerException(
192:                            "The test string must not be null.");
193:                if (newTestString.length() == 0)
194:                    throw new IllegalArgumentException(
195:                            "The test string must not be empty.");
196:
197:                String oldTestString = averageCharWidthTestString;
198:                averageCharWidthTestString = newTestString;
199:                changeSupport.firePropertyChange(
200:                        "averageCharacterWidthTestString", oldTestString,
201:                        newTestString);
202:            }
203:
204:            /**
205:             * Returns the dialog font that is used to compute the dialog base units.
206:             * If a default dialog font has been set using 
207:             * {@link #setDefaultDialogFont(Font)}, this font will be returned.
208:             * Otherwise a cached fallback will be lazily created.  
209:             * 
210:             * @return the font used to compute the dialog base units
211:             */
212:            public Font getDefaultDialogFont() {
213:                return defaultDialogFont != null ? defaultDialogFont
214:                        : getCachedDefaultDialogFont();
215:            }
216:
217:            /**
218:             * Sets a dialog font that will be used to compute the dialog base units.
219:             * 
220:             * @param newFont   the default dialog font to be set
221:             */
222:            public void setDefaultDialogFont(Font newFont) {
223:                Font oldFont = defaultDialogFont; // Don't use the getter
224:                defaultDialogFont = newFont;
225:                changeSupport.firePropertyChange("defaultDialogFont", oldFont,
226:                        newFont);
227:            }
228:
229:            // Implementing Abstract Superclass Behavior ******************************
230:
231:            /**
232:             * Returns the cached or computed horizontal dialog base units. 
233:             * 
234:             * @param component     a Component that provides the font and graphics
235:             * @return the horizontal dialog base units
236:             */
237:            protected double getDialogBaseUnitsX(Component component) {
238:                return getDialogBaseUnits(component).x;
239:            }
240:
241:            /**
242:             * Returns the cached or computed vertical dialog base units
243:             * for the given component.  
244:             * 
245:             * @param component     a Component that provides the font and graphics
246:             * @return the vertical dialog base units
247:             */
248:            protected double getDialogBaseUnitsY(Component component) {
249:                return getDialogBaseUnits(component).y;
250:            }
251:
252:            // Compute and Cache Global and Components Dialog Base Units **************
253:
254:            /**
255:             * Lazily computes and answer the global dialog base units.
256:             * Should be re-computed if the l&amp;f, platform, or screen changes. 
257:             * 
258:             * @return a cached DialogBaseUnits object used globally if no container is available 
259:             */
260:            private DialogBaseUnits getGlobalDialogBaseUnits() {
261:                if (cachedGlobalDialogBaseUnits == null) {
262:                    cachedGlobalDialogBaseUnits = computeGlobalDialogBaseUnits();
263:                }
264:                return cachedGlobalDialogBaseUnits;
265:            }
266:
267:            /**
268:             * Looks up and returns the dialog base units for the given component.
269:             * In case the component is <code>null</code> the global dialog base units
270:             * are answered.<p>
271:             * 
272:             * Before we compute the dialog base units we check whether they
273:             * have been computed and cached before - for the same component
274:             * <code>FontMetrics</code>.
275:             * 
276:             * @param c  the component that provides the graphics object
277:             * @return the DialogBaseUnits object for the given component
278:             */
279:            private DialogBaseUnits getDialogBaseUnits(Component c) {
280:                if (c == null) { // || (font = c.getFont()) == null) {
281:                    // logInfo("Missing font metrics: " + c);
282:                    return getGlobalDialogBaseUnits();
283:                }
284:                FontMetrics fm = c.getFontMetrics(getDefaultDialogFont());
285:                DialogBaseUnits dialogBaseUnits = (DialogBaseUnits) cachedDialogBaseUnits
286:                        .get(fm);
287:                if (dialogBaseUnits == null) {
288:                    dialogBaseUnits = computeDialogBaseUnits(fm);
289:                    cachedDialogBaseUnits.put(fm, dialogBaseUnits);
290:                }
291:                return dialogBaseUnits;
292:            }
293:
294:            /**
295:             * Computes and returns the horizontal dialog base units. 
296:             * Honors the font, font size and resolution.<p>
297:             * 
298:             * Implementation Note: 14dluY map to 22 pixel for 8pt Tahoma on 96 dpi.
299:             * I could not yet manage to compute the Microsoft compliant font height.
300:             * Therefore this method adds a correction value that seems to work
301:             * well with the vast majority of desktops.<p>
302:             * 
303:             * TODO: Revise the computation of vertical base units as soon as 
304:             * there are more information about the original computation 
305:             * in Microsoft environments.
306:             * 
307:             * @param metrics  the FontMetrics used to measure the dialog font
308:             * @return the horizontal and vertical dialog base units
309:             */
310:            private DialogBaseUnits computeDialogBaseUnits(FontMetrics metrics) {
311:                double averageCharWidth = computeAverageCharWidth(metrics,
312:                        averageCharWidthTestString);
313:                int ascent = metrics.getAscent();
314:                double height = ascent > 14 ? ascent : ascent + (15 - ascent)
315:                        / 3;
316:                DialogBaseUnits dialogBaseUnits = new DialogBaseUnits(
317:                        averageCharWidth, height);
318:                LOGGER.config("Computed dialog base units " + dialogBaseUnits
319:                        + " for: " + metrics.getFont());
320:                return dialogBaseUnits;
321:            }
322:
323:            /**
324:             * Computes the global dialog base units. The current implementation
325:             * assumes a fixed 8pt font and on 96 or 120 dpi. A better implementation
326:             * should ask for the main dialog font and should honor the current
327:             * screen resolution.<p>
328:             * 
329:             * Should be re-computed if the l&amp;f, platform, or screen changes.
330:             * 
331:             * @return a DialogBaseUnits object used globally if no container is available 
332:             */
333:            private DialogBaseUnits computeGlobalDialogBaseUnits() {
334:                LOGGER.config("Computing global dialog base units...");
335:                Font dialogFont = getDefaultDialogFont();
336:                FontMetrics metrics = createDefaultGlobalComponent()
337:                        .getFontMetrics(dialogFont);
338:                DialogBaseUnits globalDialogBaseUnits = computeDialogBaseUnits(metrics);
339:                return globalDialogBaseUnits;
340:            }
341:
342:            /**
343:             * Lazily creates and returns a fallback for the dialog font 
344:             * that is used to compute the dialog base units.
345:             * This fallback font is cached and will be reset if the L&amp;F changes.
346:             * 
347:             * @return the cached fallback font used to compute the dialog base units
348:             */
349:            private Font getCachedDefaultDialogFont() {
350:                if (cachedDefaultDialogFont == null) {
351:                    cachedDefaultDialogFont = lookupDefaultDialogFont();
352:                }
353:                return cachedDefaultDialogFont;
354:            }
355:
356:            /**
357:             * Looks up and returns the font used by buttons. 
358:             * First, tries to request the button font from the UIManager; 
359:             * if this fails a JButton is created and asked for its font.
360:             * 
361:             * @return the font used for a standard button
362:             */
363:            private Font lookupDefaultDialogFont() {
364:                Font buttonFont = UIManager.getFont("Button.font");
365:                return buttonFont != null ? buttonFont : new JButton()
366:                        .getFont();
367:            }
368:
369:            /**
370:             * Creates and returns a component that is used to lookup the default 
371:             * font metrics. The current implementation creates a <code>JPanel</code>.
372:             * Since this panel has no parent, it has no toolkit assigned. And so,
373:             * requesting the font metrics will end up using the default toolkit
374:             * and its deprecated method <code>ToolKit#getFontMetrics()</code>.<p>
375:             * 
376:             * TODO: Consider publishing this method and providing a setter, so that
377:             * an API user can set a realized component that has a toolkit assigned.
378:             *  
379:             * @return a component used to compute the default font metrics
380:             */
381:            private Component createDefaultGlobalComponent() {
382:                return new JPanel(null);
383:            }
384:
385:            /**
386:             * Invalidates the caches. Resets the global dialog base units,
387:             * clears the Map from <code>FontMetrics</code> to dialog base units,
388:             * and resets the fallback for the default dialog font.
389:             * This is invoked after a change of the look&amp;feel.
390:             */
391:            private void invalidateCaches() {
392:                cachedGlobalDialogBaseUnits = null;
393:                cachedDialogBaseUnits.clear();
394:                cachedDefaultDialogFont = null;
395:            }
396:
397:            // Managing Property Change Listeners **********************************
398:
399:            /**
400:             * Adds a PropertyChangeListener to the listener list. The listener is
401:             * registered for all bound properties of this class.<p>
402:             *  
403:             * If listener is null, no exception is thrown and no action is performed.
404:             *
405:             * @param listener      the PropertyChangeListener to be added
406:             *
407:             * @see #removePropertyChangeListener(PropertyChangeListener)
408:             * @see #removePropertyChangeListener(String, PropertyChangeListener)
409:             * @see #addPropertyChangeListener(String, PropertyChangeListener)
410:             */
411:            public synchronized void addPropertyChangeListener(
412:                    PropertyChangeListener listener) {
413:                changeSupport.addPropertyChangeListener(listener);
414:            }
415:
416:            /**
417:             * Removes a PropertyChangeListener from the listener list. This method
418:             * should be used to remove PropertyChangeListeners that were registered
419:             * for all bound properties of this class.<p>
420:             * 
421:             * If listener is null, no exception is thrown and no action is performed.
422:             *
423:             * @param listener      the PropertyChangeListener to be removed
424:             *
425:             * @see #addPropertyChangeListener(PropertyChangeListener)
426:             * @see #addPropertyChangeListener(String, PropertyChangeListener)
427:             * @see #removePropertyChangeListener(String, PropertyChangeListener)
428:             */
429:            public synchronized void removePropertyChangeListener(
430:                    PropertyChangeListener listener) {
431:                changeSupport.removePropertyChangeListener(listener);
432:            }
433:
434:            /**
435:             * Adds a PropertyChangeListener to the listener list for a specific
436:             * property. The specified property may be user-defined.<p>
437:             * 
438:             * Note that if this Model is inheriting a bound property, then no event
439:             * will be fired in response to a change in the inherited property.<p>
440:             * 
441:             * If listener is null, no exception is thrown and no action is performed.
442:             *
443:             * @param propertyName      one of the property names listed above
444:             * @param listener          the PropertyChangeListener to be added
445:             *
446:             * @see #removePropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
447:             * @see #addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
448:             */
449:            public synchronized void addPropertyChangeListener(
450:                    String propertyName, PropertyChangeListener listener) {
451:                changeSupport.addPropertyChangeListener(propertyName, listener);
452:            }
453:
454:            /**
455:             * Removes a PropertyChangeListener from the listener list for a specific
456:             * property. This method should be used to remove PropertyChangeListeners
457:             * that were registered for a specific bound property.<p>
458:             * 
459:             * If listener is null, no exception is thrown and no action is performed.
460:             *
461:             * @param propertyName      a valid property name
462:             * @param listener          the PropertyChangeListener to be removed
463:             *
464:             * @see #addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
465:             * @see #removePropertyChangeListener(java.beans.PropertyChangeListener)
466:             */
467:            public synchronized void removePropertyChangeListener(
468:                    String propertyName, PropertyChangeListener listener) {
469:                changeSupport.removePropertyChangeListener(propertyName,
470:                        listener);
471:            }
472:
473:            // Helper Code ************************************************************
474:
475:            /**
476:             * Describes horizontal and vertical dialog base units.
477:             */
478:            private static final class DialogBaseUnits {
479:
480:                final double x;
481:                final double y;
482:
483:                DialogBaseUnits(double dialogBaseUnitsX, double dialogBaseUnitsY) {
484:                    this .x = dialogBaseUnitsX;
485:                    this .y = dialogBaseUnitsY;
486:                }
487:
488:                public String toString() {
489:                    return "DBU(x=" + x + "; y=" + y + ")";
490:                }
491:            }
492:
493:            /**
494:             * Listens to changes of the Look and Feel and invalidates the cache.
495:             */
496:            private final class LookAndFeelChangeHandler implements 
497:                    PropertyChangeListener {
498:                public void propertyChange(PropertyChangeEvent evt) {
499:                    invalidateCaches();
500:                }
501:            }
502:
503:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.