/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* Part of the Processing project - http://processing.org Copyright (c) 2004-10 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package processing.app.tools; import processing.app.*; import processing.app.ui.Editor; import processing.app.ui.Toolkit; import processing.core.*; import java.awt.Component; import java.awt.Dimension; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsEnvironment; import java.awt.RenderingHints; import java.awt.event.*; import java.io.*; import java.util.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; /** * GUI tool for font creation heaven/hell. */ public class CreateFont extends JFrame implements Tool { Base base; JList<String> fontSelector; JTextField sizeSelector; JButton charsetButton; JCheckBox smoothBox; JComponent sample; JButton okButton; JTextField filenameField; Map<String,Font> table; boolean smooth = true; Font font; String[] list; int selection = -1; CharacterSelector charSelector; public CreateFont() { super(Language.text("create_font")); } public String getMenuTitle() { return Language.text("menu.tools.create_font"); } public void init(Base base) { this.base = base; Container paine = getContentPane(); paine.setLayout(new BorderLayout()); //10, 10)); JPanel pain = new JPanel(); pain.setBorder(new EmptyBorder(13, 13, 13, 13)); paine.add(pain, BorderLayout.CENTER); pain.setLayout(new BoxLayout(pain, BoxLayout.Y_AXIS)); String labelText = Language.text("create_font.label"); JTextArea textarea = new JTextArea(labelText); textarea.setBorder(new EmptyBorder(10, 10, 20, 10)); textarea.setBackground(null); textarea.setEditable(false); textarea.setHighlighter(null); textarea.setFont(new Font("Dialog", Font.PLAIN, 12)); pain.add(textarea); // don't care about families starting with . or # // also ignore dialog, dialoginput, monospaced, serif, sansserif // getFontList is deprecated in 1.4, so this has to be used //long t = System.currentTimeMillis(); GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); Font[] fonts = ge.getAllFonts(); //System.out.println("font startup took " + (System.currentTimeMillis() - t) + " ms"); /* if (false) { ArrayList<Font> fontList = new ArrayList<Font>(); File folderList = new File("/Users/fry/coconut/sys/fonts/web"); for (File folder : folderList.listFiles()) { if (folder.isDirectory()) { File[] items = folder.listFiles(new FilenameFilter() { public boolean accept(File dir, String name) { if (name.charAt(0) == '.') return false; return (name.toLowerCase().endsWith(".ttf") || name.toLowerCase().endsWith(".otf")); } }); for (File fontFile : items) { try { FileInputStream fis = new FileInputStream(fontFile); BufferedInputStream input = new BufferedInputStream(fis); Font font = Font.createFont(Font.TRUETYPE_FONT, input); input.close(); fontList.add(font); } catch (Exception e) { System.out.println("Ignoring " + fontFile.getName()); } } } } // fonts = new Font[fontList.size()]; // fontList.toArray(fonts); fonts = fontList.toArray(new Font[fontList.size()]); } */ String[] fontList = new String[fonts.length]; table = new HashMap<String,Font>(); int index = 0; for (int i = 0; i < fonts.length; i++) { //String psname = fonts[i].getPSName(); //if (psname == null) System.err.println("ps name is null"); try { fontList[index++] = fonts[i].getPSName(); table.put(fonts[i].getPSName(), fonts[i]); // Checking into http://processing.org/bugs/bugzilla/407.html // and https://github.com/processing/processing/issues/1727 // if (fonts[i].getPSName().contains("Helv")) { // System.out.println(fonts[i].getPSName() + " -> " + fonts[i]); // } } catch (Exception e) { // Sometimes fonts cause lots of trouble. // http://code.google.com/p/processing/issues/detail?id=442 e.printStackTrace(); } } list = new String[index]; System.arraycopy(fontList, 0, list, 0, index); fontSelector = new JList<String>(list); fontSelector.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting() == false) { selection = fontSelector.getSelectedIndex(); okButton.setEnabled(true); update(); } } }); fontSelector.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); fontSelector.setVisibleRowCount(12); JScrollPane fontScroller = new JScrollPane(fontSelector); pain.add(fontScroller); Dimension d1 = new Dimension(13, 13); pain.add(new Box.Filler(d1, d1, d1)); sample = new SampleComponent(this); // Seems that in some instances, no default font is set // http://dev.processing.org/bugs/show_bug.cgi?id=777 sample.setFont(new Font("Dialog", Font.PLAIN, 12)); pain.add(sample); Dimension d2 = new Dimension(6, 6); pain.add(new Box.Filler(d2, d2, d2)); JPanel panel = new JPanel(); panel.add(new JLabel(Language.text("create_font.size") + ":" )); sizeSelector = new JTextField(" 48 "); sizeSelector.getDocument().addDocumentListener(new DocumentListener() { public void insertUpdate(DocumentEvent e) { update(); } public void removeUpdate(DocumentEvent e) { update(); } public void changedUpdate(DocumentEvent e) { } }); panel.add(sizeSelector); smoothBox = new JCheckBox(Language.text("create_font.smooth")); smoothBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { smooth = smoothBox.isSelected(); update(); } }); smoothBox.setSelected(smooth); panel.add(smoothBox); // allBox = new JCheckBox("All Characters"); // allBox.addActionListener(new ActionListener() { // public void actionPerformed(ActionEvent e) { // all = allBox.isSelected(); // } // }); // allBox.setSelected(all); // panel.add(allBox); charsetButton = new JButton(Language.text("create_font.characters")); charsetButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { //showCharacterList(); charSelector.setVisible(true); } }); panel.add(charsetButton); pain.add(panel); JPanel filestuff = new JPanel(); filestuff.add(new JLabel(Language.text("create_font.filename") + ":")); filestuff.add(filenameField = new JTextField(20)); filestuff.add(new JLabel(".vlw")); pain.add(filestuff); JPanel buttons = new JPanel(); JButton cancelButton = new JButton(Language.text("prompt.cancel")); cancelButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setVisible(false); } }); okButton = new JButton(Language.text("prompt.ok")); okButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { build(); } }); okButton.setEnabled(false); buttons.add(cancelButton); buttons.add(okButton); pain.add(buttons); JRootPane root = getRootPane(); root.setDefaultButton(okButton); ActionListener disposer = new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { setVisible(false); } }; Toolkit.registerWindowCloseKeys(root, disposer); Toolkit.setIcon(this); pack(); setResizable(false); //System.out.println(getPreferredSize()); // do this after pack so it doesn't affect layout sample.setFont(new Font(list[0], Font.PLAIN, 48)); fontSelector.setSelectedIndex(0); // Dimension screen = Toolkit.getScreenSize(); // windowSize = getSize(); // setLocation((screen.width - windowSize.width) / 2, // (screen.height - windowSize.height) / 2); setLocationRelativeTo(null); // create this behind the scenes charSelector = new CharacterSelector(); } public void run() { setVisible(true); } public void update() { int fontsize = 0; try { fontsize = Integer.parseInt(sizeSelector.getText().trim()); //System.out.println("'" + sizeSelector.getText() + "'"); } catch (NumberFormatException e2) { } // if a deselect occurred, selection will be -1 if ((fontsize > 0) && (fontsize < 256) && (selection != -1)) { //font = new Font(list[selection], Font.PLAIN, fontsize); Font instance = table.get(list[selection]); // font = instance.deriveFont(Font.PLAIN, fontsize); font = instance.deriveFont((float) fontsize); //System.out.println("setting font to " + font); sample.setFont(font); String filenameSuggestion = list[selection].replace(' ', '_'); filenameSuggestion += "-" + fontsize; filenameField.setText(filenameSuggestion); } } public void build() { int fontsize = 0; try { fontsize = Integer.parseInt(sizeSelector.getText().trim()); } catch (NumberFormatException e) { } if (fontsize <= 0) { JOptionPane.showMessageDialog(this, "Bad font size, try again.", "Badness", JOptionPane.WARNING_MESSAGE); return; } String filename = filenameField.getText().trim(); if (filename.length() == 0) { JOptionPane.showMessageDialog(this, "Enter a file name for the font.", "Lameness", JOptionPane.WARNING_MESSAGE); return; } if (!filename.endsWith(".vlw")) { filename += ".vlw"; } try { Font instance = table.get(list[selection]); font = instance.deriveFont(Font.PLAIN, fontsize); //PFont f = new PFont(font, smooth, all ? null : PFont.CHARSET); PFont f = new PFont(font, smooth, charSelector.getCharacters()); // the editor may have changed while the window was open Editor editor = base.getActiveEditor(); // make sure the 'data' folder exists File folder = editor.getSketch().prepareDataFolder(); f.save(new FileOutputStream(new File(folder, filename))); } catch (IOException e) { JOptionPane.showMessageDialog(CreateFont.this, "An error occurred while creating font.", "No font for you", JOptionPane.WARNING_MESSAGE); e.printStackTrace(); } setVisible(false); } // /** // * make the window vertically resizable // */ // public Dimension getMaximumSize() { // return new Dimension(windowSize.width, 2000); // } // // // public Dimension getMinimumSize() { // return windowSize; // } /* public void show(File targetFolder) { this.targetFolder = targetFolder; show(); } */ } /** * Component that draws the sample text. This is its own subclassed component * because Mac OS X controls seem to reset the RenderingHints for smoothing * so that they cannot be overridden properly for JLabel or JTextArea. * @author fry */ class SampleComponent extends JComponent { // see http://rinkworks.com/words/pangrams.shtml String text = "Forsaking monastic tradition, twelve jovial friars gave up their " + "vocation for a questionable existence on the flying trapeze."; int high = 80; CreateFont parent; public SampleComponent(CreateFont p) { this.parent = p; // and yet, we still need an inner class to handle the basics. // or no, maybe i'll refactor this as a separate class! // maybe a few getters and setters? mmm? addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { String input = (String) JOptionPane.showInputDialog(parent, "Enter new sample text:", "Sample Text", JOptionPane.PLAIN_MESSAGE, null, // icon null, // choices text); if (input != null) { text = input; parent.repaint(); } } }); } public void paintComponent(Graphics g) { // System.out.println("smoothing set to " + smooth); Graphics2D g2 = (Graphics2D) g; g2.setColor(Color.WHITE); Dimension dim = getSize(); g2.fillRect(0, 0, dim.width, dim.height); g2.setColor(Color.BLACK); g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, parent.smooth ? RenderingHints.VALUE_TEXT_ANTIALIAS_ON : RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); // add this one as well (after 1.0.9) g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, parent.smooth ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF); //super.paintComponent(g2); Font font = getFont(); int ascent = g2.getFontMetrics().getAscent(); // System.out.println(f.getName()); g2.setFont(font); g2.drawString(text, 5, dim.height - (dim.height - ascent) / 2); } public Dimension getPreferredSize() { return new Dimension(400, high); } public Dimension getMaximumSize() { return new Dimension(10000, high); } public Dimension getMinimumSize() { return new Dimension(100, high); } } /** * Frame for selecting which characters will be included with the font. */ class CharacterSelector extends JFrame { JRadioButton defaultCharsButton; JRadioButton allCharsButton; JRadioButton unicodeCharsButton; JScrollPane unicodeBlockScroller; JList<JCheckBox> charsetList; public CharacterSelector() { super(Language.text("create_font.character_selector")); charsetList = new CheckBoxList(); DefaultListModel<JCheckBox> model = new DefaultListModel<JCheckBox>(); charsetList.setModel(model); for (String item : blockNames) { model.addElement(new JCheckBox(item)); } unicodeBlockScroller = new JScrollPane(charsetList, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); Container outer = getContentPane(); outer.setLayout(new BorderLayout()); JPanel pain = new JPanel(); pain.setBorder(new EmptyBorder(13, 13, 13, 13)); outer.add(pain, BorderLayout.CENTER); pain.setLayout(new BoxLayout(pain, BoxLayout.Y_AXIS)); String labelText = Language.text("create_font.character_selector.label"); JTextArea textarea = new JTextArea(labelText); textarea.setBorder(new EmptyBorder(13, 8, 13, 8)); textarea.setBackground(null); textarea.setEditable(false); textarea.setHighlighter(null); textarea.setFont(new Font("Dialog", Font.PLAIN, 12)); pain.add(textarea); ActionListener listener = new ActionListener() { public void actionPerformed(ActionEvent e) { //System.out.println("action " + unicodeCharsButton.isSelected()); //unicodeBlockScroller.setEnabled(unicodeCharsButton.isSelected()); charsetList.setEnabled(unicodeCharsButton.isSelected()); } }; defaultCharsButton = new JRadioButton(Language.text("create_font.default_characters")); allCharsButton = new JRadioButton(Language.text("create_font.all_characters")); unicodeCharsButton = new JRadioButton(Language.text("create_font.specific_unicode")); defaultCharsButton.addActionListener(listener); allCharsButton.addActionListener(listener); unicodeCharsButton.addActionListener(listener); ButtonGroup group = new ButtonGroup(); group.add(defaultCharsButton); group.add(allCharsButton); group.add(unicodeCharsButton); JPanel radioPanel = new JPanel(); //radioPanel.setBackground(Color.red); radioPanel.setLayout(new BoxLayout(radioPanel, BoxLayout.Y_AXIS)); radioPanel.add(defaultCharsButton); radioPanel.add(allCharsButton); radioPanel.add(unicodeCharsButton); JPanel rightStuff = new JPanel(); rightStuff.setLayout(new BoxLayout(rightStuff, BoxLayout.X_AXIS)); rightStuff.add(radioPanel); rightStuff.add(Box.createHorizontalGlue()); pain.add(rightStuff); pain.add(Box.createVerticalStrut(13)); // pain.add(radioPanel); // pain.add(defaultCharsButton); // pain.add(allCharsButton); // pain.add(unicodeCharsButton); defaultCharsButton.setSelected(true); charsetList.setEnabled(false); //frame.getContentPane().add(scroller); pain.add(unicodeBlockScroller); pain.add(Box.createVerticalStrut(8)); JPanel buttons = new JPanel(); JButton okButton = new JButton(Language.text("prompt.ok")); okButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setVisible(false); } }); okButton.setEnabled(true); buttons.add(okButton); pain.add(buttons); JRootPane root = getRootPane(); root.setDefaultButton(okButton); ActionListener disposer = new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { setVisible(false); } }; Toolkit.registerWindowCloseKeys(root, disposer); Toolkit.setIcon(this); pack(); Dimension screen = Toolkit.getScreenSize(); Dimension windowSize = getSize(); setLocation((screen.width - windowSize.width) / 2, (screen.height - windowSize.height) / 2); } protected char[] getCharacters() { if (defaultCharsButton.isSelected()) { return PFont.CHARSET; } char[] charset = new char[65536]; if (allCharsButton.isSelected()) { for (int i = 0; i < 0xFFFF; i++) { charset[i] = (char) i; } } else { DefaultListModel model = (DefaultListModel) charsetList.getModel(); int index = 0; for (int i = 0; i < BLOCKS.length; i++) { if (((JCheckBox) model.get(i)).isSelected()) { for (int j = blockStart[i]; j <= blockStop[i]; j++) { charset[index++] = (char) j; } } } charset = PApplet.subset(charset, 0, index); } //System.out.println("Creating font with " + charset.length + " characters."); return charset; } // http://www.unicode.org/Public/UNIDATA/Blocks.txt static final String[] BLOCKS = { "0000..007F; Basic Latin", "0080..00FF; Latin-1 Supplement", "0100..017F; Latin Extended-A", "0180..024F; Latin Extended-B", "0250..02AF; IPA Extensions", "02B0..02FF; Spacing Modifier Letters", "0300..036F; Combining Diacritical Marks", "0370..03FF; Greek and Coptic", "0400..04FF; Cyrillic", "0500..052F; Cyrillic Supplement", "0530..058F; Armenian", "0590..05FF; Hebrew", "0600..06FF; Arabic", "0700..074F; Syriac", "0750..077F; Arabic Supplement", "0780..07BF; Thaana", "07C0..07FF; NKo", "0800..083F; Samaritan", "0900..097F; Devanagari", "0980..09FF; Bengali", "0A00..0A7F; Gurmukhi", "0A80..0AFF; Gujarati", "0B00..0B7F; Oriya", "0B80..0BFF; Tamil", "0C00..0C7F; Telugu", "0C80..0CFF; Kannada", "0D00..0D7F; Malayalam", "0D80..0DFF; Sinhala", "0E00..0E7F; Thai", "0E80..0EFF; Lao", "0F00..0FFF; Tibetan", "1000..109F; Myanmar", "10A0..10FF; Georgian", "1100..11FF; Hangul Jamo", "1200..137F; Ethiopic", "1380..139F; Ethiopic Supplement", "13A0..13FF; Cherokee", "1400..167F; Unified Canadian Aboriginal Syllabics", "1680..169F; Ogham", "16A0..16FF; Runic", "1700..171F; Tagalog", "1720..173F; Hanunoo", "1740..175F; Buhid", "1760..177F; Tagbanwa", "1780..17FF; Khmer", "1800..18AF; Mongolian", "18B0..18FF; Unified Canadian Aboriginal Syllabics Extended", "1900..194F; Limbu", "1950..197F; Tai Le", "1980..19DF; New Tai Lue", "19E0..19FF; Khmer Symbols", "1A00..1A1F; Buginese", "1A20..1AAF; Tai Tham", "1B00..1B7F; Balinese", "1B80..1BBF; Sundanese", "1C00..1C4F; Lepcha", "1C50..1C7F; Ol Chiki", "1CD0..1CFF; Vedic Extensions", "1D00..1D7F; Phonetic Extensions", "1D80..1DBF; Phonetic Extensions Supplement", "1DC0..1DFF; Combining Diacritical Marks Supplement", "1E00..1EFF; Latin Extended Additional", "1F00..1FFF; Greek Extended", "2000..206F; General Punctuation", "2070..209F; Superscripts and Subscripts", "20A0..20CF; Currency Symbols", "20D0..20FF; Combining Diacritical Marks for Symbols", "2100..214F; Letterlike Symbols", "2150..218F; Number Forms", "2190..21FF; Arrows", "2200..22FF; Mathematical Operators", "2300..23FF; Miscellaneous Technical", "2400..243F; Control Pictures", "2440..245F; Optical Character Recognition", "2460..24FF; Enclosed Alphanumerics", "2500..257F; Box Drawing", "2580..259F; Block Elements", "25A0..25FF; Geometric Shapes", "2600..26FF; Miscellaneous Symbols", "2700..27BF; Dingbats", "27C0..27EF; Miscellaneous Mathematical Symbols-A", "27F0..27FF; Supplemental Arrows-A", "2800..28FF; Braille Patterns", "2900..297F; Supplemental Arrows-B", "2980..29FF; Miscellaneous Mathematical Symbols-B", "2A00..2AFF; Supplemental Mathematical Operators", "2B00..2BFF; Miscellaneous Symbols and Arrows", "2C00..2C5F; Glagolitic", "2C60..2C7F; Latin Extended-C", "2C80..2CFF; Coptic", "2D00..2D2F; Georgian Supplement", "2D30..2D7F; Tifinagh", "2D80..2DDF; Ethiopic Extended", "2DE0..2DFF; Cyrillic Extended-A", "2E00..2E7F; Supplemental Punctuation", "2E80..2EFF; CJK Radicals Supplement", "2F00..2FDF; Kangxi Radicals", "2FF0..2FFF; Ideographic Description Characters", "3000..303F; CJK Symbols and Punctuation", "3040..309F; Hiragana", "30A0..30FF; Katakana", "3100..312F; Bopomofo", "3130..318F; Hangul Compatibility Jamo", "3190..319F; Kanbun", "31A0..31BF; Bopomofo Extended", "31C0..31EF; CJK Strokes", "31F0..31FF; Katakana Phonetic Extensions", "3200..32FF; Enclosed CJK Letters and Months", "3300..33FF; CJK Compatibility", "3400..4DBF; CJK Unified Ideographs Extension A", "4DC0..4DFF; Yijing Hexagram Symbols", "4E00..9FFF; CJK Unified Ideographs", "A000..A48F; Yi Syllables", "A490..A4CF; Yi Radicals", "A4D0..A4FF; Lisu", "A500..A63F; Vai", "A640..A69F; Cyrillic Extended-B", "A6A0..A6FF; Bamum", "A700..A71F; Modifier Tone Letters", "A720..A7FF; Latin Extended-D", "A800..A82F; Syloti Nagri", "A830..A83F; Common Indic Number Forms", "A840..A87F; Phags-pa", "A880..A8DF; Saurashtra", "A8E0..A8FF; Devanagari Extended", "A900..A92F; Kayah Li", "A930..A95F; Rejang", "A960..A97F; Hangul Jamo Extended-A", "A980..A9DF; Javanese", "AA00..AA5F; Cham", "AA60..AA7F; Myanmar Extended-A", "AA80..AADF; Tai Viet", "ABC0..ABFF; Meetei Mayek", "AC00..D7AF; Hangul Syllables", "D7B0..D7FF; Hangul Jamo Extended-B", "D800..DB7F; High Surrogates", "DB80..DBFF; High Private Use Surrogates", "DC00..DFFF; Low Surrogates", "E000..F8FF; Private Use Area", "F900..FAFF; CJK Compatibility Ideographs", "FB00..FB4F; Alphabetic Presentation Forms", "FB50..FDFF; Arabic Presentation Forms-A", "FE00..FE0F; Variation Selectors", "FE10..FE1F; Vertical Forms", "FE20..FE2F; Combining Half Marks", "FE30..FE4F; CJK Compatibility Forms", "FE50..FE6F; Small Form Variants", "FE70..FEFF; Arabic Presentation Forms-B", "FF00..FFEF; Halfwidth and Fullwidth Forms", "FFF0..FFFF; Specials" }; static String[] blockNames; static int[] blockStart; static int[] blockStop; static { int count = BLOCKS.length; blockNames = new String[count]; blockStart = new int[count]; blockStop = new int[count]; for (int i = 0; i < count; i++) { String line = BLOCKS[i]; blockStart[i] = PApplet.unhex(line.substring(0, 4)); blockStop[i] = PApplet.unhex(line.substring(6, 10)); blockNames[i] = line.substring(12); } // PApplet.println(codePointStop); // PApplet.println(codePoints); } } // Code for this CheckBoxList class found on the net, though I've lost the // link. If you run across the original version, please let me know so that // the original author can be credited properly. It was from a snippet // collection, but it seems to have been picked up so many places with others // placing their copyright on it, that I haven't been able to determine the // original author. [fry 20100216] class CheckBoxList extends JList<JCheckBox> { protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1); public CheckBoxList() { setCellRenderer(new CellRenderer()); addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { if (isEnabled()) { int index = locationToIndex(e.getPoint()); if (index != -1) { JCheckBox checkbox = getModel().getElementAt(index); checkbox.setSelected(!checkbox.isSelected()); repaint(); } } } }); setSelectionMode(ListSelectionModel.SINGLE_SELECTION); } protected class CellRenderer implements ListCellRenderer { public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { JCheckBox checkbox = (JCheckBox) value; checkbox.setBackground(isSelected ? getSelectionBackground() : getBackground()); checkbox.setForeground(isSelected ? getSelectionForeground() : getForeground()); //checkbox.setEnabled(isEnabled()); checkbox.setEnabled(list.isEnabled()); checkbox.setFont(getFont()); checkbox.setFocusPainted(false); checkbox.setBorderPainted(true); checkbox.setBorder(isSelected ? UIManager.getBorder("List.focusCellHighlightBorder") : noFocusBorder); return checkbox; } } }