001    /* JAPI - (Yet another (hopefully) useful) Java API
002     *
003     * Copyright (C) 2004-2006 Christian Hujer
004     *
005     * This program is free software; you can redistribute it and/or
006     * modify it under the terms of the GNU General Public License as
007     * published by the Free Software Foundation; either version 2 of the
008     * License, or (at your option) any later version.
009     *
010     * This program is distributed in the hope that it will be useful, but
011     * WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013     * General Public License for more details.
014     *
015     * You should have received a copy of the GNU General Public License
016     * along with this program; if not, write to the Free Software
017     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
018     * 02111-1307, USA.
019     */
020    
021    package net.sf.japi.swing.font;
022    
023    import java.awt.Component;
024    import java.awt.Font;
025    import static java.awt.Font.BOLD;
026    import static java.awt.Font.ITALIC;
027    import static java.awt.Font.PLAIN;
028    import java.awt.GraphicsEnvironment;
029    import java.awt.GridBagConstraints;
030    import java.awt.GridBagLayout;
031    import java.awt.Insets;
032    import static javax.swing.BorderFactory.createCompoundBorder;
033    import static javax.swing.BorderFactory.createEmptyBorder;
034    import static javax.swing.BorderFactory.createTitledBorder;
035    import javax.swing.JComponent;
036    import javax.swing.JLabel;
037    import javax.swing.JList;
038    import static javax.swing.JOptionPane.OK_CANCEL_OPTION;
039    import static javax.swing.JOptionPane.OK_OPTION;
040    import static javax.swing.JOptionPane.PLAIN_MESSAGE;
041    import static javax.swing.JOptionPane.showConfirmDialog;
042    import javax.swing.JScrollPane;
043    import javax.swing.JSpinner;
044    import static javax.swing.ListSelectionModel.SINGLE_SELECTION;
045    import javax.swing.SpinnerNumberModel;
046    import javax.swing.event.ChangeEvent;
047    import javax.swing.event.ChangeListener;
048    import javax.swing.event.ListSelectionEvent;
049    import javax.swing.event.ListSelectionListener;
050    import org.jetbrains.annotations.Nullable;
051    import net.sf.japi.swing.ActionFactory;
052    import static net.sf.japi.swing.ActionFactory.getFactory;
053    
054    /** Class for letting the user choose a font.
055     * There are two possibilities to use this class:
056     * <ul>
057     *  <li>You can use an instance of FontChooser as a Pane and add it to the desired Container.</li>
058     *  <li>You can use this class' static methods to display a Dialog which lets the user choose a font.</li>
059     * </ul>
060     * @author <a href="mailto:chris@riedquat.de">Christian Hujer</a>
061     */
062    public class FontChooser extends JComponent implements ListSelectionListener, ChangeListener {
063    
064        /** Action Factory. */
065        private static final ActionFactory ACTION_FACTORY = getFactory("net.sf.japi.swing.font");
066    
067        /** JList for Font Family.
068         * @serial include
069         */
070        private JList familyList;
071    
072        /** JList for Font Style.
073         * @serial include
074         */
075        private JList styleList;
076    
077        /** JList for Font Size.
078         * @serial include
079         */
080        private JList sizeList;
081    
082        /** JSpinner for Font Size.
083         * @serial include
084         */
085        private JSpinner sizeSpinner;
086    
087        /** FontPreview for Font.
088         * @serial include
089         */
090        private FontPreview preview;
091    
092        /** Selected Font.
093         * @serial include
094         */
095        private Font selectedFont;
096    
097        /** Create a new FontChooser. */
098        public FontChooser() {
099            setBorder(createCompoundBorder(createCompoundBorder(createEmptyBorder(8, 8, 8, 8), createTitledBorder(ACTION_FACTORY.getString("desiredFont_borderTitle"))), createEmptyBorder(8, 4, 4, 4)));
100            setLayout(new GridBagLayout());
101            final GridBagConstraints gbc = new GridBagConstraints();
102            gbc.insets = new Insets(2, 2, 2, 2);
103            final JLabel familyLabel = ACTION_FACTORY.createLabel("family.label");
104            final JLabel styleLabel = ACTION_FACTORY.createLabel("style.label");
105            final JLabel sizeLabel = ACTION_FACTORY.createLabel("size.label");
106            familyList = new JList(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames());
107            styleList = new JList(new Integer[] { PLAIN, ITALIC, BOLD, BOLD|ITALIC });
108            styleList.setCellRenderer(new FontStyleListCellRenderer());
109            sizeList = new JList(new Integer[] { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 32, 48, 64 });
110            preview = new FontPreview();
111            sizeSpinner = new JSpinner(new SpinnerNumberModel(12, 4, 100, 1));
112            gbc.weightx = 1.0;
113            gbc.fill = GridBagConstraints.BOTH;
114            add(familyLabel, gbc);
115            add(styleLabel, gbc);
116            gbc.gridwidth = GridBagConstraints.REMAINDER;
117            add(sizeLabel, gbc);
118            gbc.gridwidth = 1;
119            gbc.gridheight = 2;
120            gbc.weighty = 1.0;
121            add(new JScrollPane(familyList), gbc);
122            add(new JScrollPane(styleList), gbc);
123            gbc.gridheight = 1;
124            gbc.gridwidth = GridBagConstraints.REMAINDER;
125            gbc.weighty = 0.0;
126            add(sizeSpinner, gbc);
127            gbc.weighty = 1.0;
128            add(new JScrollPane(sizeList), gbc);
129            gbc.gridwidth = 3;
130            add(preview, gbc);
131            familyList.addListSelectionListener(this);
132            styleList.addListSelectionListener(this);
133            sizeList.addListSelectionListener(this);
134            sizeSpinner.addChangeListener(this);
135            familyList.setSelectionMode(SINGLE_SELECTION);
136            styleList.setSelectionMode(SINGLE_SELECTION);
137            sizeList.setSelectionMode(SINGLE_SELECTION);
138        }
139    
140        /** Set the selected font. */
141        public void setSelectedFont(final Font selectedFont) {
142            this.selectedFont = selectedFont;
143            preview.setFont(selectedFont);
144            //lock = true;
145            sizeSpinner.setValue(selectedFont.getSize());
146            sizeList.setSelectedValue(selectedFont.getSize(), true);
147            styleList.setSelectedValue(selectedFont.getStyle(), true);
148            familyList.setSelectedValue(selectedFont.getFamily(), true);
149            //lock = false;
150        }
151    
152        /** Set the selected family. */
153        private void updateFont() {
154            //if (lock) { return; }
155            final String family = familyList.getSelectedValue() == null ? selectedFont.getFamily() : (String) familyList.getSelectedValue();
156            final int style = styleList.getSelectedValue() == null ? selectedFont.getStyle() : (Integer) styleList.getSelectedValue();
157            final int size = sizeList.getSelectedValue() == null ? selectedFont.getSize() : (Integer) sizeSpinner.getValue();
158            selectedFont = new Font(family, style, size);
159            preview.setFont(selectedFont);
160        }
161    
162        /** {@inheritDoc} */
163        public void valueChanged(final ListSelectionEvent e) {
164            final Object source = e.getSource();
165            if     (source == familyList) {
166                // No special action except updateFont()
167            } else if (source == styleList) {
168                // No special action except updateFont()
169            } else if (source == sizeList) {
170                final Object size = sizeList.getSelectedValue();
171                if (!sizeSpinner.getValue().equals(size) && size != null) {
172                    sizeSpinner.setValue(size);
173                }
174            } else {
175                assert false;
176            }
177            updateFont();
178        }
179    
180        /** {@inheritDoc} */
181        public void stateChanged(final ChangeEvent e) {
182            final Object source = e.getSource();
183            if (source == sizeSpinner) {
184                final Object size = sizeSpinner.getValue();
185                if (!size.equals(sizeList.getSelectedValue())) {
186                    sizeList.setSelectedValue(size, true);
187                }
188            } else {
189                assert false;
190            }
191            updateFont();
192        }
193    
194        /** Show a dialog.
195         * @param parent Parent component
196         * @return seleced font or null
197         */
198        public static Font showChooseFontDialog(final Component parent) {
199            return showChooseFontDialog(parent, Font.decode(null));
200        }
201    
202        /** Show a dialog.
203         * @param parent Parent compnent
204         * @param font Font to modify
205         * @return selected font or null
206         */
207        @Nullable public static Font showChooseFontDialog(final Component parent, final Font font) {
208            final FontChooser chooser = new FontChooser();
209            chooser.setSelectedFont(font);
210            if (showConfirmDialog(parent, chooser, ACTION_FACTORY.getString("chooser.title"), OK_CANCEL_OPTION, PLAIN_MESSAGE) == OK_OPTION) {
211                return chooser.selectedFont;
212            } else {
213                return null;
214            }
215        }
216    
217    } // class FontChooser