/*
 * FontPicker - applet/application to display user-selected fonts
 *
 * Copyright 1999-2013, Lee Brown.  All rights reserved.
 * Permission to use is granted without warranty on the condition that the copyright
 * information and author's name be retained in the code.
 */
 
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JApplet;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;

/**
 * The FontPicker class provides controls for selecting a font (family,
 * style, and point size) plus foreground and background colors.  After
 * each selection, a text area is redisplayed with the new attributes.
 * <p>
 * This class can be run as an applet or application.  It requires JDK 1.2
 * (to use the getAvailableFontFamilyNames() method of GraphicsEnvironment)
 * but has most recently been built with 1.6.0_21.
 *
 * @author Lee Brown
 * @version JDK 1.2, April 1999
 */
 
public class FontPicker
	extends JApplet
	implements ActionListener, ItemListener
{
	private static final String APP_TITLE = "Font Picker";
	
	private boolean applet;
	private JTextArea text;
	private JComboBox families;
	private JComboBox sizes;
	private JComboBox styles;
	private JTextField fgcolor;
	private JTextField bgcolor;

	/*
	 * The styleNames correspond to the font style values defined in the
	 * Font class, mapping names to those values.
	 */
	private static final String[] styleNames = {
		"Plain",
		"Bold",
		"Italic",
		"Bold italic"
	};

	/*
	 * The colorNames and colorValues arrays must correspond.  They map
	 * user-specified names to values (obviously).
	 */
	private static final String[] colorNames = {
		"black",
		"blue",
		"cyan",
		"darkGray",
		"gray",
		"green",
		"lightGray",
		"magenta",
		"orange",
		"pink",
		"red",
		"white",
		"yellow"
	};

	private static final Color[] colorValues = {
		Color.black,
		Color.blue,
		Color.cyan,
		Color.darkGray,
		Color.gray,
		Color.green,
		Color.lightGray,
		Color.magenta,
		Color.orange,
		Color.pink,
		Color.red,
		Color.white,
		Color.yellow
	};

	private static final int MINPTSIZE = 6;
	private static final int MAXPTSIZE = 36;
	private static final int DFLTPTSIZE = 14;
	private static final String DFLTFAMILY = "Arial";


	/**
	 * The null constructor is used by applets to call the real constructor
	 * with a flag that indicates that this invocation is an applet.
	 */
	public FontPicker()
	{
		this(true);
	}


	/**
	 * This constructor is used directly by applications, and indirectly by
	 * applets.  The parameter indicates whether the invocation is an applet
	 * (if true) or application (if false).
	 *
	 * @param appletFlag true for applets, false for applications
	 */
	protected FontPicker(boolean appletFlag)
	{
		applet = appletFlag;

		if (applet)
		{
			getRootPane().putClientProperty("defeatSystemEventQueueCheck", Boolean.TRUE);
		}
	}


	/**
	 * The init method does all the work of creating the user interface.  At the
	 * time of the applet constructor, the windowing environment is not yet
	 * ready for use.
	 */
	public void init()
	{
		int i;

		setBackground(Color.gray);

		// This will be the font used at the beginning.
		
		Font f = new Font(DFLTFAMILY, Font.PLAIN, DFLTPTSIZE);

		// Start creating the control area.
		
		JPanel panel = new JPanel();
		if (applet)
			panel.setBorder(BorderFactory.createCompoundBorder(
				BorderFactory.createRaisedBevelBorder(),
				BorderFactory.createEmptyBorder(10, 10, 10, 10)));
		else
			panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
		panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
		getContentPane().add(panel);

		text = new JTextArea();
		text.setBorder(BorderFactory.createLoweredBevelBorder());
		text.setFont(f);
		text.setForeground(Color.black);
		text.setBackground(Color.white);
		text.setPreferredSize(new Dimension(320, 100));
		text.setText("The quick brown fox jumped\nover the lazy dog.");

		JPanel panel2 = new JPanel();
		panel2.setLayout(new BoxLayout(panel2, BoxLayout.X_AXIS));
		JPanel panel3 = new JPanel();
		panel3.setLayout(new BoxLayout(panel3, BoxLayout.X_AXIS));
		
		families = new JComboBox();
		GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
		String[] familyNames = ge.getAvailableFontFamilyNames();
		for (i = 0; i < familyNames.length; i++)
		{
			if (familyNames[i].indexOf('.') >= 0)
				continue;
			families.addItem(familyNames[i]);
			if (familyNames[i].equals(DFLTFAMILY))
				families.setSelectedIndex(i);
		}
		families.addItemListener(this);
		panel2.add(families);
		panel2.add(Box.createHorizontalGlue());
		panel2.add(Box.createHorizontalStrut(6));

		sizes = new JComboBox();
		for (i = MINPTSIZE; i <= MAXPTSIZE; i++)
		{
			sizes.addItem(String.valueOf(i));
		}
		sizes.setSelectedIndex(DFLTPTSIZE-MINPTSIZE);
		sizes.addItemListener(this);
		panel2.add(sizes);
		panel2.add(Box.createHorizontalStrut(6));
		panel2.add(Box.createHorizontalGlue());

		styles = new JComboBox();
		for (i = 0; i < styleNames.length; i++)
		{
			styles.addItem(styleNames[i]);
		}
		styles.setSelectedIndex(Font.PLAIN);
		styles.addItemListener(this);
		panel2.add(styles);

		fgcolor = new JTextField(8);
		fgcolor.setText("black");
		fgcolor.setFocusAccelerator('F');
		fgcolor.addActionListener(this);
		
		JLabel fglabel = new JLabel("Foreground");
		fglabel.setLabelFor(fgcolor);
		fglabel.setDisplayedMnemonic('F');
		panel3.add(fglabel);
		panel3.add(Box.createHorizontalStrut(4));
		panel3.add(fgcolor);
		panel3.add(Box.createHorizontalGlue());
		panel3.add(Box.createHorizontalStrut(15));
		
		bgcolor = new JTextField(8);
		bgcolor.setText("white");
		bgcolor.setFocusAccelerator('B');
		bgcolor.addActionListener(this);
		
		JLabel bglabel = new JLabel("Background");
		bglabel.setLabelFor(bgcolor);
		bglabel.setDisplayedMnemonic('B');
		panel3.add(bglabel);
		panel3.add(Box.createHorizontalStrut(4));
		panel3.add(bgcolor);

		panel.add(text);
		panel.add(Box.createVerticalStrut(8));
		panel.add(panel2);
		panel.add(Box.createVerticalStrut(8));
		panel.add(panel3);

		bgcolor.setMaximumSize(bgcolor.getPreferredSize());
		fgcolor.setMaximumSize(fgcolor.getPreferredSize());
	}
	

	/**
	 * Convert a color string into a Color object.  If the color string cannot
	 * be converted, the indicated default color will be used.  A color string
	 * can be a color name or a hex string begun with a '#', e.g., "#33cc33".
	 *
	 * @param s String representation of the color
	 * @param defaultColor default color used if the color won't parse
	 * @return Color object
	 */
	private Color parseColor(String s, Color defaultColor)
	{
		Color c = defaultColor;
		
		s.trim();
		if (s.startsWith("#"))
		{
			s = "0x" + s.substring(1);
			try
			{
				c = Color.decode(s);
			}
			catch (NumberFormatException nfe) {}
		}
		else
		{
			for (int i = 0; i < colorNames.length; i++)
			{
				if (s.equalsIgnoreCase(colorNames[i]))
				{
					c = colorValues[i];
					break;
				}
			}
		}

		return c;
	}


	/**
	 * Handle the Return key in either of the color text fields.  Go ahead and
	 * change the color of the output text area, which will repaint.
	 *
	 * @param e Action event for color text fields
	 */
	public void actionPerformed(ActionEvent e)
	{
		Color bg = parseColor(bgcolor.getText(), Color.white);
		Color fg = parseColor(fgcolor.getText(), Color.black);
		text.setBackground(bg);
		text.setForeground(fg);
	}


	/**
	 * Handle drop-down menu selections for font family name, style, and size.
	 * Go ahead and change the font for the output text area, which will repaint.
	 * It appears that changing only the style will have no effect on the text
	 * area font until some other attribute is changed.
	 *
	 * @param e Item event for drop-down combo boxes
	 */
	public void itemStateChanged(ItemEvent e)
	{
		if (e.getStateChange() == ItemEvent.SELECTED)
		{
			if (e.getSource() == families || e.getSource() == styles || e.getSource() == sizes)
			{
				String fname = (String) families.getSelectedItem();
				int fstyle = styles.getSelectedIndex();
				int fsize = sizes.getSelectedIndex() + 6;
				Font f = new Font(fname, fstyle, fsize);
				if (f == null)
				{
					System.err.println("null font: " + fname + ' ' + fstyle + ' ' + fsize + "pt");
				}
				else
				{
					text.setFont(f);
					text.repaint();
				}
			}
		}
	}


	/**
	 * Application main program.  Call the constructor and use the init
	 * method to create the window.
	 *
	 * @param args command line arguments (not used)
	 */
	public static void main(String[] args)
	{
		JFrame frame = new JFrame(APP_TITLE);

		frame.addWindowListener(new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				System.exit(0);
			}
		});

		FontPicker picker = new FontPicker(false);
		picker.init();

		frame.getContentPane().add(picker, BorderLayout.CENTER);
		frame.pack();
		frame.setVisible(true);
		System.out.println("FontPicker: " + picker + ", w=" + frame.getWidth() + " h=" + frame.getHeight());
	}
}
