package chatty.gui.components.settings;
import chatty.Helper;
import chatty.util.StringUtil;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Locale;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
/**
*
* @author tduva
*/
public class FontChooser extends JDialog implements InputListListener,
ListSelectionListener, ActionListener {
// Lists
private final InputList fontList;
private final InputList fontSizeList;
private static final String PREVIEW_TEXT =
"<html><body style=\"margin: 4px;\">"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ<br />"
+ "abcdefghijklmnopqrstuvwxyz<br />"
+ "1234567890<br />"
+ "ÄÖÜäöüôàúåãë.,-\\/#+-µ[:]{}$";
// Preview
private final FontPreviewLabel preview = new FontPreviewLabel(PREVIEW_TEXT);
private final JTextField test = new JTextField();
// Buttons
private final JButton ok = new JButton("Ok");
private final JButton cancel = new JButton("Cancel");
// Close Action
private int closeAction;
public static final int ACTION_OK = 1;
public static final int ACTION_CANCEL = 0;
// Font
private Font font;
private static final int FALLBACK_FONT_SIZE = 14;
private String[] fontSizes = new String[]{
"8", "9", "10", "11", "12", "13", "14", "15", "16", "18", "20", "22",
"24", "26", "28", "30", "32", "36", "48", "60", "72"
};
// Reference
private final Dialog owner;
/**
* Creates a FontChooser with the given Dialog as owner.
*
* @param owner
*/
public FontChooser(Dialog owner) {
super(owner,"Choose font",true);
this.owner = owner;
setLayout(new GridBagLayout());
setMinimumSize(new Dimension(300,500));
ok.addActionListener(this);
cancel.addActionListener(this);
/*
* Font selection (Panel)
*/
JPanel fontSelection = new JPanel(new GridLayout(1,2));
// Create and fill font list
String[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().
getAvailableFontFamilyNames();
fontList = new InputList(fonts);
// Create and fill font size list
fontSizeList = new InputList(fontSizes, new IntegerVerifier());
fontSelection.add(fontList);
fontSelection.add(fontSizeList);
fontSelection.setBorder(BorderFactory.createTitledBorder("Select font family and size"));
// List listeners
fontList.addInputListListener(this);
fontSizeList.addInputListListener(this);
/*
* Font preview (Panel)
*/
JPanel fontPreview = new JPanel(new BorderLayout());
fontPreview.add(preview);
fontPreview.setBorder(BorderFactory.createTitledBorder("Preview"));
/*
* Font test (Panel)
*/
JPanel fontTest = new JPanel(new BorderLayout());
fontTest.add(test);
test.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
updatePreviewText();
}
@Override
public void removeUpdate(DocumentEvent e) {
updatePreviewText();
}
@Override
public void changedUpdate(DocumentEvent e) {
updatePreviewText();
}
});
fontTest.setBorder(BorderFactory.createTitledBorder("Enter additional text to test"));
/*
* Add everything to the dialog
*/
// Panels
GridBagConstraints gbc;
gbc = makeGbc(0,0,2,1);
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 1;
gbc.weighty = 0.7;
add(fontSelection,gbc);
gbc = makeGbc(0,1,2,1);
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 1;
gbc.weighty = 0.3;
add(fontPreview,gbc);
gbc = makeGbc(0,2,2,1);
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1;
gbc.weighty = 0;
add(fontTest,gbc);
// Buttons
gbc = makeGbc(0,3,1,1);
gbc.anchor = GridBagConstraints.EAST;
gbc.weightx = 0.5;
add(ok,gbc);
gbc = makeGbc(1,3,1,1);
gbc.weightx = 0.5;
gbc.anchor = GridBagConstraints.WEST;
add(cancel,gbc);
pack();
}
public String getFontName() {
return font.getName();
}
public Integer getFontSize() {
return font.getSize();
}
/**
* Open the dialog with the given initial settings.
*
* @param defaultFont
* @param defaultFontSize
* @return
*/
public int showDialog(String defaultFont, int defaultFontSize) {
fontList.setValue(defaultFont);
fontSizeList.setValue(new Integer(defaultFontSize).toString());
closeAction = ACTION_CANCEL;
setLocationRelativeTo(owner);
setVisible(true);
return closeAction;
}
/**
* Creates a new Font based on the current settings and also updates the
* preview.
*
* The created Font is not only used for the preview, but also to return
* it when requested from the caller.
*/
private void updateFont() {
String fontName = fontList.getValue();
Integer fontSize = FALLBACK_FONT_SIZE;
try {
fontSize = Integer.parseInt(fontSizeList.getValue());
} catch (NumberFormatException ex) {
// Just use the fallback font size
}
font = new Font(fontName,Font.PLAIN,fontSize);
preview.setFont(font);
}
private void updatePreviewText() {
preview.setText(PREVIEW_TEXT+"<br />"+test.getText());
}
private GridBagConstraints makeGbc(int x, int y, int w, int h) {
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = x;
gbc.gridy = y;
gbc.gridwidth = w;
gbc.gridheight = h;
gbc.insets = new Insets(5, 5, 5, 5);
return gbc;
}
@Override
public void valueChanged(ListSelectionEvent e) {
updateFont();
}
@Override
public void valueChanged(String value) {
updateFont();
}
/**
* When a Button was pressed, close Dialog, change closeAction when the
* Ok-Button was pressed.
*
* @param e
*/
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == ok) {
closeAction = ACTION_OK;
}
setVisible(false);
}
static class FontPreviewLabel extends JLabel {
FontPreviewLabel(String previewText) {
setPreferredSize(new Dimension(300,100));
//setBackground(Color.WHITE);
//setForeground(Color.black);
//setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
//setOpaque(true);
setText(previewText);
}
}
/**
* A JList combined with a JTextField.
*/
static class InputList extends JPanel implements ListSelectionListener,
ActionListener, DocumentListener {
private JList<String> list;
private JTextField input;
private InputListListener listener;
InputList(String[] data) {
this(data, null);
}
InputList(String[] data, InputVerifier inputVerifier) {
// List
list = new JList<>(data);
list.addListSelectionListener(this);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JScrollPane listScroll = new JScrollPane(list);
// Text input
input = new JTextField();
input.setInputVerifier(inputVerifier);
input.addActionListener(this);
input.getDocument().addDocumentListener(this);
setLayout(new BorderLayout());
/*
* Add everything to the panel
*/
add(input, BorderLayout.NORTH);
add(listScroll);
}
public void addInputListListener(InputListListener listener) {
this.listener = listener;
}
public String getValue() {
return input.getText();
}
public void setValue(String value) {
list.clearSelection();
list.setSelectedValue(value, true);
input.setText(value);
}
@Override
public void actionPerformed(ActionEvent e) {
valueChanged(input.getText());
}
private void valueChanged(String newValue) {
if (listener != null) {
listener.valueChanged(newValue);
}
}
/**
* Sets the text of the input field based on the current selected value
* of the list.
*/
private void updateTextInput() {
String newValue = list.getSelectedValue();
if (newValue != null && !newValue.isEmpty()) {
input.setText(newValue);
valueChanged(newValue);
}
}
/**
* Given on the current text input, scroll to the appropriate entry or
* select it if it's an exact match.
*/
private void updateListSelection() {
String inputValue = StringUtil.toLowerCase(input.getText());
if (inputValue.isEmpty()) {
return;
}
int size = list.getModel().getSize();
for (int i=0;i < size;i++) {
String value = list.getModel().getElementAt(i);
if (value.equalsIgnoreCase(inputValue)) {
list.setSelectedValue(value, true);
valueChanged(value);
break;
}
if (StringUtil.toLowerCase(value).startsWith(inputValue)) {
scrollToValue(value);
break;
}
}
}
private void scrollToValue(String value) {
list.setSelectedValue(value, true);
list.clearSelection();
}
/*
* Text input listeners
*/
@Override
public void insertUpdate(DocumentEvent e) {
updateListSelection();
}
@Override
public void removeUpdate(DocumentEvent e) {
updateListSelection();
}
@Override
public void changedUpdate(DocumentEvent e) {
}
/*
* List selection changed
*/
@Override
public void valueChanged(ListSelectionEvent e) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
updateTextInput();
}
});
}
}
static class IntegerVerifier extends InputVerifier {
@Override
public boolean verify(JComponent input) {
JTextField component = (JTextField)input;
try {
Integer.parseInt(component.getText());
return true;
} catch (NumberFormatException ex) {
return false;
}
}
}
}
interface InputListListener {
void valueChanged(String value);
}