/* * Copyright (C) 2007 Clam <clamisgood@gmail.com> * Copyright (C) 2008 IsmAvatar <IsmAvatar@gmail.com> * Copyright (C) 2008, 2009 Quadduc <quadduc@gmail.com> * Copyright (C) 2014, Robert B. Colton * * This file is part of LateralGM. * LateralGM is free software and comes with ABSOLUTELY NO WARRANTY. * See LICENSE for details. */ package org.lateralgm.subframes; import static java.lang.Integer.MAX_VALUE; import static javax.swing.GroupLayout.DEFAULT_SIZE; import java.awt.Component; import java.awt.Dimension; import java.awt.GraphicsEnvironment; import java.awt.event.ActionEvent; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import javax.swing.BorderFactory; import javax.swing.GroupLayout; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.KeyStroke; import javax.swing.ListCellRenderer; import javax.swing.GroupLayout.Alignment; import javax.swing.GroupLayout.SequentialGroup; import javax.swing.ScrollPaneConstants; import javax.swing.border.EmptyBorder; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.ListSelectionModel; import org.lateralgm.components.CustomFileChooser; import org.lateralgm.components.NumberField; import org.lateralgm.components.impl.ResNode; import org.lateralgm.main.LGM; import org.lateralgm.main.Prefs; import org.lateralgm.main.UpdateSource.UpdateEvent; import org.lateralgm.main.UpdateSource.UpdateListener; import org.lateralgm.messages.Messages; import org.lateralgm.resources.Font; import org.lateralgm.resources.Font.PFont; import org.lateralgm.resources.sub.CharacterRange; import org.lateralgm.resources.sub.CharacterRange.PCharacterRange; import org.lateralgm.subframes.RoomFrame.ListComponentRenderer; import org.lateralgm.ui.swing.propertylink.FormattedLink; import org.lateralgm.ui.swing.propertylink.PropertyLinkFactory; import org.lateralgm.ui.swing.propertylink.ComboBoxLink.IndexComboBoxConversion; import org.lateralgm.ui.swing.util.ArrayListModel; import org.lateralgm.util.AutoComboBox; import org.lateralgm.util.PropertyLink; import org.lateralgm.util.PropertyMap.PropertyUpdateEvent; import org.lateralgm.util.PropertyMap.PropertyUpdateListener; public class FontFrame extends InstantiableResourceFrame<Font,PFont> implements ListSelectionListener,ListDataListener,UpdateListener { private static final long serialVersionUID = 1L; public JComboBox<String> fonts; public NumberField size; public JCheckBox italic, bold; public JComboBox<String> aa; public NumberField charMin, charMax; private FormattedLink<PCharacterRange> minLink, maxLink; public JEditorPane previewText; public JTextArea previewRange; private JMenuItem cutItem, copyItem, pasteItem, selAllItem; private CharacterRange lastRange = null; //non-guaranteed copy of rangeList.getLastSelectedValue() public JList<CharacterRange> rangeList; private PropertyUpdateListener<PFont> propUpdateListener; @Override public void dispose() { super.dispose(); res.properties.updateSource.removeListener(propUpdateListener); res.rangeUpdateSource.removeListener(this); } public FontFrame(Font res, ResNode node) { super(res,node); ((JComponent) getContentPane()).setBorder(new EmptyBorder(4,4,4,4)); propUpdateListener = new PropertyUpdateListener<PFont>() { public void updated(PropertyUpdateEvent<PFont> e) { updatePreviewText(); updatePreviewRange(); } }; res.properties.updateSource.addListener(propUpdateListener); res.rangeUpdateSource.addListener(this); GroupLayout layout = new GroupLayout(getContentPane()); layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(false); setLayout(layout); JLabel lName = new JLabel(Messages.getString("FontFrame.NAME")); //$NON-NLS-1$ JLabel lFont = new JLabel(Messages.getString("FontFrame.FONT")); //$NON-NLS-1$ fonts = new AutoComboBox<String>( GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()); //fonts.setEditable(true); plf.make(fonts,PFont.FONT_NAME,null); JLabel lSize = new JLabel(Messages.getString("FontFrame.SIZE")); //$NON-NLS-1$ size = new NumberField(1,99); plf.make(size,PFont.SIZE); bold = new JCheckBox(Messages.getString("FontFrame.BOLD")); //$NON-NLS-1$ plf.make(bold,PFont.BOLD); italic = new JCheckBox(Messages.getString("FontFrame.ITALIC")); //$NON-NLS-1$ plf.make(italic,PFont.ITALIC); String aaprefix = "FontFrame.AA"; //$NON-NLS-1$ String aalevels[] = new String[4]; for (int i = 0; i < aalevels.length; i++) aalevels[i] = Messages.getString(aaprefix + i); aa = new JComboBox<String>(aalevels); plf.make(aa, PFont.ANTIALIAS, new IndexComboBoxConversion()); JLabel aaLabel = new JLabel(Messages.getString("FontFrame.ANTIALIAS")); //$NON-NLS-1$ JPanel crPane = makeCRPane(); previewText = new JEditorPane(); previewRange = new JTextArea(); previewRange.setEditable(false); // show the caret anyway previewRange.addFocusListener(new FocusListener() { @Override public void focusGained(FocusEvent e) { previewRange.getCaret().setVisible(true); previewRange.getCaret().setSelectionVisible(true); } @Override public void focusLost(FocusEvent e) { previewRange.getCaret().setVisible(false); previewRange.getCaret().setSelectionVisible(false); } }); previewRange.setWrapStyleWord(false); rangeList = new JList<CharacterRange>(); rangeList.setModel(new ArrayListModel<CharacterRange>(res.characterRanges)); rangeList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); RangeListComponentRenderer renderer = new RangeListComponentRenderer(); rangeList.setCellRenderer(renderer); rangeList.getModel().addListDataListener(this); rangeList.addListSelectionListener(this); rangeList.setSelectedIndex(0); rangeList.setLayoutOrientation(JList.VERTICAL); rangeList.setVisibleRowCount(6); JButton fromPreview = new JButton(Messages.getString("FontFrame.FROMPREVIEW")); fromPreview.setActionCommand("FromPreview"); //$NON-NLS-1$ fromPreview.addActionListener(this); JButton fromString = new JButton(Messages.getString("FontFrame.FROMSTRING")); fromString.setActionCommand("FromString"); //$NON-NLS-1$ fromString.addActionListener(this); JButton fromFile = new JButton(Messages.getString("FontFrame.FROMFILE")); fromFile.setActionCommand("FromFile"); //$NON-NLS-1$ fromFile.addActionListener(this); JButton addRange = new JButton("+"); addRange.setActionCommand("Add"); //$NON-NLS-1$ addRange.addActionListener(this); JButton remRange = new JButton("-"); remRange.setActionCommand("Remove"); //$NON-NLS-1$ remRange.addActionListener(this); JButton clearRange = new JButton(Messages.getString("FontFrame.CLEAR")); clearRange.setActionCommand("Clear"); //$NON-NLS-1$ clearRange.addActionListener(this); JScrollPane listScroller = new JScrollPane(rangeList); listScroller.setPreferredSize(new Dimension(250,80)); add(listScroller); previewText.setText(Messages.getString("FontFrame.PREVIEW_DEFAULT")); makeContextMenu(); JScrollPane previewTextScroll = new JScrollPane(previewText); JScrollPane previewRangeScroll = new JScrollPane(previewRange); previewRangeScroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); previewText.setSize(500,500); updatePreviewText(); updatePreviewRange(); save.setText(Messages.getString("FontFrame.SAVE")); //$NON-NLS-1$ SequentialGroup orientationGroup = layout.createSequentialGroup(); if (Prefs.rightOrientation) { orientationGroup.addGroup( layout.createParallelGroup().addComponent(previewTextScroll,0,500,MAX_VALUE).addComponent( previewRangeScroll,0,500,MAX_VALUE)); } orientationGroup.addGroup(layout.createParallelGroup().addGroup(layout.createSequentialGroup() /* */.addGroup(layout.createParallelGroup(Alignment.TRAILING) /* */.addComponent(lName) /* */.addComponent(lFont) /* */.addComponent(lSize)) /* */.addGroup(layout.createParallelGroup() /* */.addComponent(name,DEFAULT_SIZE,120,MAX_VALUE) /* */.addComponent(fonts,120,160,MAX_VALUE) /* */.addComponent(size))) /**/.addGroup(layout.createSequentialGroup() /* */.addComponent(aaLabel).addComponent(aa) /* */.addComponent(bold) /* */.addComponent(italic)) /**/.addComponent(crPane) /**/.addGroup(layout.createSequentialGroup() /* */.addComponent(fromPreview) /* */.addComponent(fromString) /* */.addComponent(fromFile)) /**/.addGroup(layout.createSequentialGroup() /* */.addComponent(addRange) /* */.addComponent(remRange) /* */.addComponent(clearRange)) /**/.addGroup(layout.createSequentialGroup() /**/.addComponent(listScroller,120,220,MAX_VALUE)) /**/.addComponent(save,DEFAULT_SIZE,DEFAULT_SIZE,MAX_VALUE)); if (!Prefs.rightOrientation) { orientationGroup.addGroup( layout.createParallelGroup().addComponent(previewTextScroll,0,500,MAX_VALUE).addComponent( previewRangeScroll,0,500,MAX_VALUE)); } layout.setHorizontalGroup(orientationGroup); layout.setVerticalGroup(layout.createParallelGroup().addGroup(layout.createSequentialGroup() /**/.addGroup(layout.createParallelGroup(Alignment.BASELINE) /* */.addComponent(lName) /* */.addComponent(name)) /**/.addGroup(layout.createParallelGroup(Alignment.BASELINE) /* */.addComponent(lFont) /* */.addComponent(fonts)) /**/.addGroup(layout.createParallelGroup(Alignment.BASELINE) /* */.addComponent(lSize) /* */.addComponent(size)) /**/.addGroup(layout.createParallelGroup(Alignment.BASELINE) /* */.addComponent(aa).addComponent(aaLabel) /* */.addComponent(bold) /* */.addComponent(italic)) /**/.addComponent(crPane) /**/.addGroup(layout.createParallelGroup(Alignment.BASELINE) /* */.addComponent(fromPreview) /* */.addComponent(fromString) /* */.addComponent(fromFile)) /**/.addGroup(layout.createParallelGroup(Alignment.BASELINE) /* */.addComponent(addRange) /* */.addComponent(remRange) /* */.addComponent(clearRange)) /**/.addGroup(layout.createParallelGroup(Alignment.BASELINE) /**/.addComponent(listScroller,DEFAULT_SIZE,120,MAX_VALUE)) /**/.addComponent(save)).addGroup( layout.createSequentialGroup().addComponent(previewTextScroll,DEFAULT_SIZE,100,MAX_VALUE).addComponent( previewRangeScroll,DEFAULT_SIZE,100,MAX_VALUE))); pack(); } public JMenuItem addItem(String key) { JMenuItem item = new JMenuItem(Messages.getString(key)); item.setIcon(LGM.getIconForKey(key)); item.setActionCommand(key); item.addActionListener(this); return item; } public void makeContextMenu() { // build popup menu JPopupMenu popup = new JPopupMenu(); cutItem = addItem("FontFrame.CUT"); //$NON-NLS-1$ cutItem.setAccelerator(KeyStroke.getKeyStroke(Messages.getKeyboardString("FontFrame.CUT"))); popup.add(cutItem); copyItem = addItem("FontFrame.COPY"); //$NON-NLS-1$ copyItem.setAccelerator(KeyStroke.getKeyStroke(Messages.getKeyboardString("FontFrame.COPY"))); popup.add(copyItem); pasteItem = addItem("FontFrame.PASTE"); //$NON-NLS-1$ pasteItem.setAccelerator(KeyStroke.getKeyStroke(Messages.getKeyboardString("FontFrame.PASTE"))); popup.add(pasteItem); popup.addSeparator(); selAllItem = addItem("FontFrame.SELECTALL"); //$NON-NLS-1$ selAllItem.setAccelerator(KeyStroke.getKeyStroke(Messages.getKeyboardString("FontFrame.SELECTALL"))); popup.add(selAllItem); previewText.setComponentPopupMenu(popup); popup = new JPopupMenu(); copyItem = addItem("FontFrame.COPY"); //$NON-NLS-1$ copyItem.setAccelerator(KeyStroke.getKeyStroke(Messages.getKeyboardString("FontFrame.CUT"))); copyItem.setActionCommand("COPYRANGE"); popup.add(copyItem); popup.addSeparator(); selAllItem = addItem("FontFrame.SELECTALL"); //$NON-NLS-1$ selAllItem.setAccelerator(KeyStroke.getKeyStroke(Messages.getKeyboardString("FontFrame.SELECTALL"))); selAllItem.setActionCommand("SELECTALLRANGE"); popup.add(selAllItem); previewRange.setComponentPopupMenu(popup); } private JPanel makeCRPane() { JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createTitledBorder(Messages.getString("FontFrame.CHARRANGE"))); GroupLayout layout = new GroupLayout(panel); layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); panel.setLayout(layout); charMin = new NumberField(0,Integer.MAX_VALUE); charMin.setCommitsOnValidEdit(true); //charMin.addValueChangeListener(this); //plf.make(charMin,PFont.RANGE_MIN); JLabel lTo = new JLabel(Messages.getString("FontFrame.TO")); //$NON-NLS-1$ charMax = new NumberField(0,Integer.MAX_VALUE); charMax.setCommitsOnValidEdit(true); //charMin.addValueChangeListener(this); //plf.make(charMax,PFont.RANGE_MAX); JButton crNormal = new JButton(Messages.getString("FontFrame.NORMAL")); //$NON-NLS-1$ crNormal.setActionCommand("Normal"); //$NON-NLS-1$ crNormal.addActionListener(this); JButton crAll = new JButton(Messages.getString("FontFrame.ASCII")); //$NON-NLS-1$ crAll.setActionCommand("ASCII"); //$NON-NLS-1$ crAll.addActionListener(this); JButton crDigits = new JButton(Messages.getString("FontFrame.DIGITS")); //$NON-NLS-1$ crDigits.setActionCommand("Digits"); //$NON-NLS-1$ crDigits.addActionListener(this); JButton crLetters = new JButton(Messages.getString("FontFrame.LETTERS")); //$NON-NLS-1$ crLetters.setActionCommand("Letters"); //$NON-NLS-1$ crLetters.addActionListener(this); layout.setAutoCreateContainerGaps(false); layout.setHorizontalGroup(layout.createParallelGroup() /**/.addGroup(layout.createSequentialGroup() /* */.addComponent(charMin) /* */.addComponent(lTo) /* */.addComponent(charMax)) /**/.addGroup(layout.createSequentialGroup() /* */.addGroup(layout.createParallelGroup() /* */.addComponent(crNormal,DEFAULT_SIZE,DEFAULT_SIZE,MAX_VALUE) /* */.addComponent(crDigits,DEFAULT_SIZE,DEFAULT_SIZE,MAX_VALUE)) /* */.addGroup(layout.createParallelGroup() /* */.addComponent(crAll,DEFAULT_SIZE,DEFAULT_SIZE,MAX_VALUE) /* */.addComponent(crLetters,DEFAULT_SIZE,DEFAULT_SIZE,MAX_VALUE)))); layout.setVerticalGroup(layout.createSequentialGroup() /**/.addGroup(layout.createParallelGroup(Alignment.BASELINE) /* */.addComponent(charMin) /* */.addComponent(lTo) /* */.addComponent(charMax)) /**/.addGroup(layout.createSequentialGroup() /* */.addGroup(layout.createParallelGroup() /* */.addComponent(crNormal) /* */.addComponent(crAll)) /* */.addGroup(layout.createParallelGroup() /* */.addComponent(crDigits) /* */.addComponent(crLetters)))); return panel; } protected boolean areResourceFieldsEqual() { return res.characterRanges.equals(resOriginal.characterRanges); } public void commitChanges() { charMin.commitOrRevert(); charMax.commitOrRevert(); res.setName(name.getText()); } public void actionPerformed(ActionEvent ev) { String com = ev.getActionCommand(); if (com.equals("Normal")) //$NON-NLS-1$ { CharacterRange cr = rangeList.getSelectedValue(); if (cr != null) { cr.properties.put(PCharacterRange.RANGE_MIN,32); cr.properties.put(PCharacterRange.RANGE_MAX,127); } return; } else if (com.equals("ASCII")) //$NON-NLS-1$ { CharacterRange cr = rangeList.getSelectedValue(); if (cr != null) { cr.properties.put(PCharacterRange.RANGE_MIN,0); cr.properties.put(PCharacterRange.RANGE_MAX,255); } return; } else if (com.equals("Digits")) //$NON-NLS-1$ { CharacterRange cr = rangeList.getSelectedValue(); if (cr != null) { cr.properties.put(PCharacterRange.RANGE_MIN,48); cr.properties.put(PCharacterRange.RANGE_MAX,57); } return; } else if (com.equals("Letters")) //$NON-NLS-1$ { CharacterRange cr = rangeList.getSelectedValue(); if (cr != null) { cr.properties.put(PCharacterRange.RANGE_MIN,65); cr.properties.put(PCharacterRange.RANGE_MAX,122); } return; } else if (com.equals("FromPreview")) { res.addRangesFromString(previewText.getText()); return; } else if (com.equals("FromString")) { String result = JOptionPane.showInputDialog(this,"","Character Sequence", JOptionPane.PLAIN_MESSAGE); if (result != null) { res.addRangesFromString(result); } return; } else if (com.equals("FromFile")) { CustomFileChooser fc = new CustomFileChooser("/org/lateralgm","LAST_FILE_DIR"); fc.setMultiSelectionEnabled(false); if (fc.showOpenDialog(LGM.frame) == JFileChooser.APPROVE_OPTION) res.addRangesFromFile(fc.getSelectedFile()); return; } else if (com.equals("Add")) { res.addRange(); return; } else if (com.equals("Remove")) { int sel = rangeList.getSelectedIndex(); if (rangeList.getSelectedValue() != null) { res.characterRanges.remove(sel); } return; } else if (com.equals("Clear")) { res.characterRanges.clear(); return; } else if (com.equals("FontFrame.CUT")) //$NON-NLS-1$ { previewText.cut(); return; } else if (com.equals("FontFrame.COPY")) //$NON-NLS-1$ { previewText.copy(); return; } else if (com.equals("FontFrame.PASTE")) //$NON-NLS-1$ { previewText.paste(); return; } else if (com.equals("FontFrame.SELECTALL")) //$NON-NLS-1$ { previewText.selectAll(); return; } else if (com.equals("SELECTALLRANGE")) //$NON-NLS-1$ { previewRange.selectAll(); return; } else if (com.equals("COPYRANGE")) //$NON-NLS-1$ { previewRange.copy(); return; } super.actionPerformed(ev); } public void updatePreviewText() { previewText.setFont(res.getAWTFont()); } public void updatePreviewRange() { String text = ""; for (CharacterRange cr : res.characterRanges) { int min = cr.properties.get(PCharacterRange.RANGE_MIN); int max = cr.properties.get(PCharacterRange.RANGE_MAX); // NOTE: Arbitrarily limit a single range to no more than 1000 sequential characters to stop // our editor from lagging like GM's if (max - min > 1000 || max < min) { max = min + 1000; } for (int i = min; i <= max; i++) { // TODO: Replace new line character with just an empty space, // otherwise it will screw up word wrapping in the preview area. if (i == '\n') { text += ' '; continue; } text += new String(Character.toChars(i)); } text += "\n"; } previewRange.setText(text); previewRange.setFont(res.getAWTFont()); } private static class RangeListComponentRenderer implements ListCellRenderer<CharacterRange> { private final JLabel lab = new JLabel(); private final ListComponentRenderer lcr = new ListComponentRenderer(); public RangeListComponentRenderer() { lab.setOpaque(true); } public Component getListCellRendererComponent(JList<? extends CharacterRange> list, CharacterRange val, int ind, boolean selected, boolean focus) { CharacterRange i = (CharacterRange) val; lcr.getListCellRendererComponent(list,lab,ind,selected,focus); lab.setText(" " + i.properties.get(PCharacterRange.RANGE_MIN) + " " + Messages.getString("FontFrame.TO") + " " + i.properties.get(PCharacterRange.RANGE_MAX)); return lab; } } public void fireRangeUpdate() { CharacterRange cr = rangeList.getSelectedValue(); if (lastRange == cr) return; lastRange = cr; PropertyLink.removeAll(minLink,maxLink); if (cr != null) { PropertyLinkFactory<PCharacterRange> rplf = new PropertyLinkFactory<PCharacterRange>( cr.properties,this); minLink = rplf.make(charMin,PCharacterRange.RANGE_MIN); maxLink = rplf.make(charMax,PCharacterRange.RANGE_MAX); } } public void updated(UpdateEvent e) { if (e.source == res.rangeUpdateSource) rangeList.setPrototypeCellValue(null); updatePreviewRange(); } public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) return; if (e.getSource() == rangeList) fireRangeUpdate(); } public void contentsChanged(ListDataEvent arg0) { updatePreviewRange(); } public void intervalAdded(ListDataEvent arg0) { updatePreviewRange(); } public void intervalRemoved(ListDataEvent arg0) { updatePreviewRange(); } }