001package jmri.util.swing;
002
003import java.awt.Component;
004import java.awt.GraphicsEnvironment;
005import java.awt.Rectangle;
006import javax.swing.JComboBox;
007import javax.swing.JList;
008import javax.swing.ListModel;
009
010/**
011 * Common utility methods for working with JComboBoxes.
012 * <p>
013 * To do vertical sizing of <u>empty</u> JComboBoxen,
014 * this will create a dummy object and cast it to the
015 * contents' type.  This can fail.
016 *
017 * @author Bob Jacobsen Copyright 2003, 2010
018 * @since 4.9.5
019 */
020public class JComboBoxUtil {
021
022    /**
023     * Set the maximum number of rows for a JComboBox so that it always can fit
024     * on the screen
025     * <p>
026     * To do vertical sizing of <u>empty</u> JComboBoxen,
027     * this will create a temporary String and use that to set a default height based
028     * on the current font settings.
029     *
030     * @param <E>        type of JComboBox contents
031     * @param <T>        subclass of JComboBox being setup
032     * @param inComboBox the JComboBox to setup
033     */
034    public static <E extends Object, T extends JComboBox<E>> void setupComboBoxMaxRows(T inComboBox) {
035        int maxItemHeight = 12; // pick some absolute minimum here
036
037        if (inComboBox.getItemCount() == 0 || (inComboBox.getItemCount() == 1 && "".equals(inComboBox.getItemAt(0)))) {
038            maxItemHeight = getDefaultRowHeight(maxItemHeight);
039        }
040
041        ListModel<E> lm = inComboBox.getModel();
042        JList<E> list = new JList<>(lm);
043
044        for (int i = 0; i < lm.getSize(); ++i) {
045            E value = lm.getElementAt(i);
046            Component c = list.getCellRenderer().getListCellRendererComponent(list, value, i, false, false);
047            maxItemHeight = Math.max(maxItemHeight, c.getPreferredSize().height);
048        }
049        // Compensate for slightly undersized cell height for macOS
050        // The last rows will be off the screen if the dock is hidden
051        if (jmri.util.SystemType.isMacOSX()) maxItemHeight++;
052
053        int itemsPerScreen = inComboBox.getItemCount();
054        // calculate the number of items that will fit on the screen
055        if (!GraphicsEnvironment.isHeadless()) {
056            // note: this line returns the maximum available size, accounting all
057            // taskbars etc. no matter where they are aligned:
058            Rectangle maxWindowBounds = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
059            itemsPerScreen = (int) maxWindowBounds.getHeight() / maxItemHeight;
060        }
061
062        int c = Math.max(itemsPerScreen - 1, 8);
063
064        // Adjust max rows if the Preferences => Display setting is greater than zero.
065        int maxRows = jmri.InstanceManager.getDefault(jmri.util.gui.GuiLafPreferencesManager.class).getMaxComboRows();
066        if (maxRows > 0) {
067            c = Math.min(c, maxRows);
068        }
069
070        inComboBox.setMaximumRowCount(c);
071    }
072
073    /**
074     * Set a default row height if the related combo box is empty.
075     * @param minimumHeight The default minimum height.
076     * @return a new height based on sample text.
077     */
078    private static int getDefaultRowHeight(int minimumHeight) {
079        String[] data = {"XYZxyz"};
080        JList<String> list = new JList<>(data);
081        String value = list.getModel().getElementAt(0);
082        Component c = list.getCellRenderer().getListCellRendererComponent(list, value, 0, false, false);
083        return Math.max(minimumHeight, c.getPreferredSize().height);
084    }
085
086//     private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(JComboBoxUtil.class);
087}