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
031package com.jgoodies.forms.util;
032
033import java.awt.Component;
034import java.awt.FontMetrics;
035import java.awt.Toolkit;
036
037/**
038 * An abstract implementation of the {@link UnitConverter} interface that
039 * minimizes the effort required to convert font-dependent sizes to pixels.
040 *
041 * @author Karsten Lentzsch
042 * @version $Revision$
043 *
044 * @see    DefaultUnitConverter
045 * @see    com.jgoodies.forms.layout.Size
046 * @see    com.jgoodies.forms.layout.Sizes
047 */
048public abstract class AbstractUnitConverter implements UnitConverter {
049
050    private static final int DTP_RESOLUTION = 72;
051
052    // Unit Converter Implementation *********************************************
053
054    /**
055     * Converts Inches and returns pixels using the specified resolution.
056     *
057     * @param in         the Inches
058     * @param component  the component that provides the graphics object
059     * @return the given Inches as pixels
060     */
061    @Override
062    public int inchAsPixel(double in, Component component) {
063        return inchAsPixel(in, getScreenResolution(component));
064    }
065
066    /**
067     * Converts Millimeters and returns pixels using the resolution of the
068     * given component's graphics object.
069     *
070     * @param mm            Millimeters
071     * @param component    the component that provides the graphics object
072     * @return the given Millimeters as pixels
073     */
074    @Override
075    public int millimeterAsPixel(double mm, Component component) {
076        return millimeterAsPixel(mm, getScreenResolution(component));
077    }
078
079    /**
080     * Converts Centimeters and returns pixels using the resolution of the
081     * given component's graphics object.
082     *
083     * @param cm            Centimeters
084     * @param component    the component that provides the graphics object
085     * @return the given Centimeters as pixels
086     */
087    @Override
088    public int centimeterAsPixel(double cm, Component component) {
089        return centimeterAsPixel(cm, getScreenResolution(component));
090    }
091
092    /**
093     * Converts DTP Points and returns pixels using the resolution of the
094     * given component's graphics object.
095     *
096     * @param pt            DTP Points
097     * @param component    the component that provides the graphics object
098     * @return the given Points as pixels
099     */
100    @Override
101    public int pointAsPixel(int pt, Component component) {
102        return pointAsPixel(pt, getScreenResolution(component));
103    }
104
105    /**
106     * Converts horizontal dialog units and returns pixels.
107     * Honors the resolution, dialog font size, platform, and l&f.
108     *
109     * @param dluX  the horizontal dialog units
110     * @param c         a Component that provides the font and graphics
111     * @return the given horizontal dialog units as pixels
112     */
113    @Override
114    public int dialogUnitXAsPixel(int dluX, Component c) {
115        return dialogUnitXAsPixel(dluX, getDialogBaseUnitsX(c));
116    }
117
118    /**
119     * Converts vertical dialog units and returns pixels.
120     * Honors the resolution, dialog font size, platform, and l&f.
121     *
122     * @param dluY  the vertical dialog units
123     * @param c     a Component that provides the font and graphics
124     * @return the given vertical dialog units as pixels
125     */
126    @Override
127    public int dialogUnitYAsPixel(int dluY, Component c) {
128        return dialogUnitYAsPixel(dluY, getDialogBaseUnitsY(c));
129    }
130
131    // Abstract Behavior *****************************************************
132
133    /**
134     * Gets and returns the horizontal dialog base units.
135     * Implementations are encouraged to cache previously computed
136     * dialog base units.
137     *
138     * @param component   a Component that provides the font and graphics
139     * @return the horizontal dialog base units
140     */
141    protected abstract double getDialogBaseUnitsX(Component component);
142
143    /**
144     * Gets and returns the vertical dialog base units.
145     * Implementations are encouraged to cache previously computed
146     * dialog base units.
147     *
148     * @param component   a Component that provides the font and graphics
149     * @return the vertical dialog base units
150     */
151    protected abstract double getDialogBaseUnitsY(Component component);
152
153    // Convenience Methods ***************************************************
154
155    /**
156     * Converts Inches and returns pixels using the specified resolution.
157     *
158     * @param in    the Inches
159     * @param dpi   the resolution
160     * @return the given Inches as pixels
161     */
162    protected final int inchAsPixel(double in, int dpi) {
163        return (int) Math.round(dpi * in);
164    }
165
166    /**
167     * Converts Millimeters and returns pixels using the specified resolution.
168     *
169     * @param mm    Millimeters
170     * @param dpi   the resolution
171     * @return the given Millimeters as pixels
172     */
173    protected final int millimeterAsPixel(double mm, int dpi) {
174        return (int) Math.round(dpi * mm * 10 / 254);
175    }
176
177    /**
178     * Converts Centimeters and returns pixels using the specified resolution.
179     *
180     * @param cm    Centimeters
181     * @param dpi   the resolution
182     * @return the given Centimeters as pixels
183     */
184    protected final int centimeterAsPixel(double cm, int dpi) {
185        return (int) Math.round(dpi * cm * 100 / 254);
186    }
187
188    /**
189     * Converts DTP Points and returns pixels using the specified resolution.
190     *
191     * @param pt    DTP Points
192     * @param dpi   the resolution in dpi
193     * @return the given Points as pixels
194     */
195    protected final int pointAsPixel(int pt, int dpi) {
196        return Math.round(dpi * pt / DTP_RESOLUTION);
197    }
198
199    /**
200     * Converts horizontal dialog units and returns pixels.
201     *
202     * @param dluX                  the horizontal dialog units
203     * @param dialogBaseUnitsX      the horizontal dialog base units
204     * @return the given dialog base units as pixels
205     */
206    protected int dialogUnitXAsPixel(int dluX, double dialogBaseUnitsX) {
207        return (int) Math.round(dluX * dialogBaseUnitsX / 4);
208    }
209
210    /**
211     * Converts vertical dialog units and returns pixels.
212     *
213     * @param dluY                  the vertical dialog units
214     * @param dialogBaseUnitsY      the vertical dialog base units
215     * @return the given dialog base units as pixels
216     */
217    protected int dialogUnitYAsPixel(int dluY, double dialogBaseUnitsY) {
218        return (int) Math.round(dluY * dialogBaseUnitsY / 8);
219    }
220
221    // Helper Code ************************************************************
222
223    /**
224     * Computes and returns the average character width
225     * of the specified test string using the given FontMetrics.
226     * The test string shall represent an "average" text.
227     *
228     * @param metrics     used to compute the test string's width
229     * @param testString  the string that shall represent an "average" text
230     * @return the test string's average character width.
231     */
232    protected double computeAverageCharWidth(FontMetrics metrics,
233            String testString) {
234        int width = metrics.stringWidth(testString);
235        double average = (double) width / testString.length();
236        //System.out.println("Average width of '" + testString + "'=" + average);
237        return average;
238    }
239
240    /**
241     * Returns the components screen resolution or the default screen
242     * resolution if the component is null or has no toolkit assigned yet.
243     *
244     * @param c the component to ask for a toolkit
245     * @return the component's screen resolution
246     */
247    protected int getScreenResolution(Component c) {
248        if (c == null) {
249            return getDefaultScreenResolution();
250        }
251
252        Toolkit toolkit = c.getToolkit();
253        return toolkit != null ? toolkit.getScreenResolution()
254                : getDefaultScreenResolution();
255    }
256
257    private static int defaultScreenResolution = -1;
258
259    /**
260     * Computes and returns the default resolution.
261     *
262     * @return the default screen resolution
263     */
264    protected int getDefaultScreenResolution() {
265        if (defaultScreenResolution == -1) {
266            defaultScreenResolution = Toolkit.getDefaultToolkit()
267                    .getScreenResolution();
268        }
269        return defaultScreenResolution;
270    }
271
272}