/* * This programs uses the information found in the UIManager * to create a table of key/value pairs for each Swing component. */ import com.weis.darklaf.DarkLafInfo; import com.weis.darklaf.LafManager; import com.weis.darklaf.ui.cell.DarkCellRendererToggleButton; import com.weis.darklaf.ui.table.DarkColorTableCellRendererEditor; import org.jdesktop.swingx.JXTaskPane; import org.jdesktop.swingx.JXTaskPaneContainer; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import javax.swing.plaf.ColorUIResource; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.TreeMap; import java.util.Vector; public class UIManagerDefaults implements ItemListener { private static final String[] COLUMN_NAMES = {"Key", "Value", "Sample"}; private static String selectedItem; private final JComponent contentPane; private final TreeMap> items; private final HashMap models; private JMenuBar menuBar; private JComboBox comboBox; private JRadioButton byComponent; private JTable table; /* * Constructor */ private UIManagerDefaults() { new JXTaskPaneContainer(); new JXTaskPane(); items = new TreeMap<>(); models = new HashMap<>(); contentPane = new JPanel(new BorderLayout()); contentPane.add(buildNorthComponent(), BorderLayout.NORTH); contentPane.add(buildCenterComponent(), BorderLayout.CENTER); resetComponents(); } public static void main(final String[] args) { UIManager.installLookAndFeel(new DarkLafInfo()); LafManager.install(); SwingUtilities.invokeLater(UIManagerDefaults::createAndShowGUI); } /* * Build a GUI using the content pane and menu bar of UIManagerDefaults */ private static void createAndShowGUI() { final UIManagerDefaults application = new UIManagerDefaults(); final JFrame frame = new JFrame("UIManager Defaults"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setJMenuBar(application.getMenuBar()); frame.getContentPane().add(application.getContentPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } /* * A menu can also be added which provides the ability to switch * between different LAF's. */ private JMenuBar getMenuBar() { if (menuBar == null) { menuBar = createMenuBar(); } return menuBar; } /* * The content pane should be added to a high level container */ @NotNull private JComponent getContentPane() { return contentPane; } /** * Create menu bar */ @NotNull private JMenuBar createMenuBar() { final JMenuBar menuBar = new JMenuBar(); menuBar.add(createFileMenu()); menuBar.add(createLAFMenu()); return menuBar; } /** * Create menu items for the Application menu */ @NotNull private JMenu createFileMenu() { final JMenu menu = new JMenu("Application"); menu.setMnemonic('A'); var item = new ExitAction(); menu.add(item); return menu; } /** * Create menu items for the Look & Feel menu */ @NotNull private JMenu createLAFMenu() { final ButtonGroup bg = new ButtonGroup(); final JMenu menu = new JMenu("Look & Feel"); menu.setMnemonic('L'); final String lafId = UIManager.getLookAndFeel().getID(); final UIManager.LookAndFeelInfo[] lafInfo = UIManager.getInstalledLookAndFeels(); for (final UIManager.LookAndFeelInfo lookAndFeelInfo : lafInfo) { final String laf = lookAndFeelInfo.getClassName(); final String name = lookAndFeelInfo.getName(); final Action action = new ChangeLookAndFeelAction(this, laf, name); final JRadioButtonMenuItem mi = new JRadioButtonMenuItem(action); menu.add(mi); bg.add(mi); if (name.equals(lafId)) { mi.setSelected(true); } } return menu; } /* * This panel is added to the North of the content pane */ @NotNull private JComponent buildNorthComponent() { comboBox = new JComboBox<>() { @Override public Dimension getPreferredSize() { return new Dimension(200, getUI().getPreferredSize(this).height); } }; final JLabel label = new JLabel("Select Item:"); label.setDisplayedMnemonic('S'); label.setLabelFor(comboBox); ItemListener itemListener = e -> { selectedItem = null; if (table.isEditing()) { table.getCellEditor().stopCellEditing(); } table.clearSelection(); resetComponents(); }; byComponent = new JRadioButton("By Component", true); byComponent.setMnemonic('C'); byComponent.addItemListener(itemListener); final JRadioButton byValueType = new JRadioButton("By Value Type"); byValueType.setMnemonic('V'); byValueType.addItemListener(itemListener); final ButtonGroup group = new ButtonGroup(); group.add(byComponent); group.add(byValueType); final JPanel panel = new JPanel(); panel.setBorder(new EmptyBorder(15, 0, 15, 0)); panel.add(label); panel.add(comboBox); panel.add(byComponent); panel.add(byValueType); return panel; } /* * This panel is added to the Center of the content pane */ @Contract(" -> new") @NotNull private JComponent buildCenterComponent() { final DefaultTableModel model = new DefaultTableModel(COLUMN_NAMES, 0); table = new JTable(model); table.setAutoCreateColumnsFromModel(false); table.setShowHorizontalLines(false); table.getColumnModel().getColumn(0).setPreferredWidth(250); table.getColumnModel().getColumn(1).setPreferredWidth(500); table.getColumnModel().getColumn(2).setPreferredWidth(100); table.getColumnModel().getColumn(2).setCellRenderer(new SampleRenderer()); table.getColumnModel().getColumn(2).setCellEditor(new DarkColorTableCellRendererEditor()); final Dimension d = table.getPreferredSize(); d.height = 350; table.setPreferredScrollableViewportSize(d); return new JScrollPane(table); } /* * When the LAF is changed we need to reset the content pane */ private void resetComponents() { items.clear(); models.clear(); ((DefaultTableModel) table.getModel()).setRowCount(0); buildItemsMap(); final Vector comboBoxItems = new Vector<>(50); for (final Object key : items.keySet()) { comboBoxItems.add((String) key); } comboBox.removeItemListener(this); comboBox.setModel(new DefaultComboBoxModel<>(comboBoxItems)); comboBox.setSelectedIndex(-1); comboBox.addItemListener(this); if (selectedItem != null) { comboBox.setSelectedItem(selectedItem); } } /* * The item map will contain items for each component or * items for each attribute type. */ private void buildItemsMap() { final UIDefaults defaults = UIManager.getLookAndFeelDefaults(); // Build of Map of items and a Map of attributes for each item for (final Object key : new HashSet<>(defaults.keySet())) { final Object value = defaults.get(key); final String itemName = getItemName(key.toString(), value); if (itemName == null) { continue; } // Get the attribute map for this componenent, or // create a map when one is not found final TreeMap attributeMap = items.computeIfAbsent(itemName, k -> new TreeMap<>()); // Add the attribute to the map for this componenent attributeMap.put(key.toString(), value); } } /* * Parse the key to determine the item name to use */ @Nullable private String getItemName(@NotNull final String key, final Object value) { // Seems like this is an old check required for JDK1.4.2 if (key.startsWith("class") || key.startsWith("javax")) { return null; } if (byComponent.isSelected()) { return getComponentName(key, value); } else { return getValueName(key, value); } } private String getComponentName(@NotNull final String key, final Object value) { // The key is of the form: // "componentName.componentProperty", or // "componentNameUI", or // "someOtherString" String componentName; final int pos = componentNameEndOffset(key); if (pos != -1) { componentName = key.substring(0, pos); } else if (key.endsWith("UI")) { componentName = key.substring(0, key.length() - 2); } else if (value instanceof ColorUIResource) { componentName = "System Colors"; } else { componentName = "Miscellaneous"; } // Fix inconsistency if (componentName.equals("Checkbox")) { componentName = "CheckBox"; } return componentName; } private int componentNameEndOffset(@NotNull final String key) { // Handle Nimbus properties first // "ComboBox.scrollPane", "Table.editor" and "Tree.cellEditor" // have different format even within the Nimbus properties. // (the component name is specified in quotes) if (key.startsWith("\"")) { return key.indexOf("\"", 1) + 1; } int pos = key.indexOf(":"); if (pos != -1) { return pos; } pos = key.indexOf("["); if (pos != -1) { return pos; } // Handle normal properties return key.indexOf("."); } @NotNull private String getValueName(@NotNull final String key, final Object value) { if (value instanceof Icon) { return "Icon"; } else if (value instanceof Font) { return "Font"; } else if (value instanceof Border) { return "Border"; } else if (value instanceof Color) { return "Color"; } else if (value instanceof Insets) { return "Insets"; } else if (value instanceof Boolean) { return "Boolean"; } else if (value instanceof Dimension) { return "Dimension"; } else if (value instanceof Number) { return "Number"; } else if (value instanceof Painter) { return "Painter"; } else if (key.endsWith("UI")) { return "UI"; } else if (key.endsWith("InputMap")) { return "InputMap"; } else if (key.endsWith("RightToLeft")) { return "InputMap"; } else if (key.endsWith("radient")) { return "Gradient"; } else { return "The Rest"; } } /* * Implement the ItemListener interface */ public void itemStateChanged(@NotNull final ItemEvent e) { final String itemName = (String) e.getItem(); changeTableModel(itemName); updateRowHeights(); selectedItem = itemName; } /* * Change the TableModel in the table for the selected item */ private void changeTableModel(final String itemName) { // The model has been created previously so just use it DefaultTableModel model = models.get(itemName); if (model != null) { table.setModel(model); return; } // Create a new model for the requested item // and addAtHead the attributes of the item to the model model = new DefaultTableModel(COLUMN_NAMES, 0); final Map attributes = items.get(itemName); for (final Object o : attributes.keySet()) { final String attribute = (String) o; Object value = attributes.get(attribute); final Vector row = new Vector<>(3); row.add(attribute); if (value != null) { row.add(value instanceof Boolean ? value : value.toString()); if (value instanceof Icon) { value = new SafeIcon((Icon) value); } row.add(value); } else { row.add("null"); row.add(""); } model.addRow(row); } table.setModel(model); models.put(itemName, model); } /* * Some rows containing icons, may need to be sized taller to fully * display the icon. */ private void updateRowHeights() { try { for (int row = 0; row < table.getRowCount(); row++) { int rowHeight = table.getRowHeight(); for (int column = 0; column < table.getColumnCount(); column++) { final Component comp = table.prepareRenderer(table.getCellRenderer(row, column), row, column); rowHeight = Math.max(rowHeight, comp.getPreferredSize().height); } table.setRowHeight(row, rowHeight); } } catch (@NotNull final ClassCastException ignored) { } } /** * Thanks to Jeanette for the use of this code found at: * *

https://jdnc-incubator.dev.java.net/source/browse/jdnc-incubator/src/kleopatra/java/org * /jdesktop/swingx/renderer/UIPropertiesViewer.java?rev=1.2&view=markup * *

Some ui-icons misbehave in that they unconditionally class-cast to the component type they * are mostly painted on. Consequently they blow up if we are trying to paint them anywhere else * (f.i. in a renderer). * *

This Icon is an adaption of a cool trick by Darryl Burke found at * http://tips4java.wordpress.com/2008/12/18/icon-table-cell-renderer * *

The base idea is to instantiate a component of the type expected by the icon, let it paint * into the graphics of a bufferedImage and create an ImageIcon from it. In subsequent calls the * ImageIcon is used. */ private static final class SafeIcon implements Icon { private final Icon wrappee; private Icon standIn; @Contract(pure = true) private SafeIcon(final Icon wrappee) { this.wrappee = wrappee; } @Override public void paintIcon(final Component c, @NotNull final Graphics g, final int x, final int y) { if (standIn == this) { paintFallback(c, g, x, y); } else if (standIn != null) { standIn.paintIcon(c, g, x, y); } else { try { wrappee.paintIcon(c, g, x, y); } catch (@NotNull final ClassCastException e) { createStandIn(e); standIn.paintIcon(c, g, x, y); } } } @Override public int getIconWidth() { return wrappee.getIconWidth(); } @Override public int getIconHeight() { return wrappee.getIconHeight(); } private void paintFallback( final Component c, @NotNull final Graphics g, final int x, final int y) { g.drawRect(x, y, getIconWidth(), getIconHeight()); g.drawLine(x, y, x + getIconWidth(), y + getIconHeight()); g.drawLine(x + getIconWidth(), y, x, y + getIconHeight()); } private void createStandIn(@NotNull final ClassCastException e) { try { final Class clazz = getClass(e); final JComponent standInComponent = getSubstitute(clazz); standIn = createImageIcon(standInComponent); } catch (@NotNull final Exception e1) { // something went wrong - fallback to this painting standIn = this; } } private Class getClass(@NotNull final ClassCastException e) throws ClassNotFoundException { String className = e.getMessage(); className = className.substring(className.lastIndexOf(" ") + 1); return Class.forName(className); } private JComponent getSubstitute(@NotNull final Class clazz) throws IllegalAccessException { JComponent standInComponent = null; try { standInComponent = (JComponent) clazz.getDeclaredConstructor().newInstance(); } catch (@NotNull final InstantiationException e) { standInComponent = new AbstractButton() { }; ((AbstractButton) standInComponent).setModel(new DefaultButtonModel()); } catch (NoSuchMethodException | InvocationTargetException e) { e.printStackTrace(); } return standInComponent; } @Contract("_ -> new") @NotNull private Icon createImageIcon(final JComponent standInComponent) { final BufferedImage image = new BufferedImage(getIconWidth(), getIconHeight(), BufferedImage.TYPE_INT_ARGB); final Graphics g = image.createGraphics(); try { wrappee.paintIcon(standInComponent, g, 0, 0); return new ImageIcon(image); } finally { g.dispose(); } } } /* * Render the value based on its class. */ private static final class SampleRenderer extends JLabel implements TableCellRenderer { private final DarkCellRendererToggleButton booleanRenderer = new DarkCellRendererToggleButton(new DarkCellRendererToggleButton.CellEditorCheckBox()); private SampleRenderer() { super(); setHorizontalAlignment(SwingConstants.CENTER); setOpaque(true); } @Contract("_, _, _, _, _, _ -> this") @NotNull public Component getTableCellRendererComponent( final JTable table, final Object sample, final boolean isSelected, final boolean hasFocus, final int row, final int column) { setBackground(null); setBorder(null); setIcon(null); setText(""); if (sample instanceof Color) { setBackground((Color) sample); } else if (sample instanceof Border) { setBorder((Border) sample); } else if (sample instanceof Font) { setText("Sample"); setFont((Font) sample); } else if (sample instanceof Icon) { setIcon((Icon) sample); } else if (sample instanceof Boolean) { return booleanRenderer.getTableCellRendererComponent(table, sample, isSelected, hasFocus, row, column); } return this; } /* * Some icons are painted using inner classes and are not meant to be * shared by other items. This code will catch the * ClassCastException that is thrown. */ public void paint(final Graphics g) { try { super.paint(g); } catch (@NotNull final Exception e) { e.printStackTrace(); } } } /* * Change the LAF and recreate the UIManagerDefaults so that the properties * of the new LAF are correctly displayed. */ private static final class ChangeLookAndFeelAction extends AbstractAction { private final UIManagerDefaults defaults; private final String laf; private ChangeLookAndFeelAction(final UIManagerDefaults defaults, final String laf, final String name) { this.defaults = defaults; this.laf = laf; putValue(Action.NAME, name); putValue(Action.SHORT_DESCRIPTION, getValue(Action.NAME)); } public void actionPerformed(@NotNull final ActionEvent e) { try { UIManager.setLookAndFeel(laf); defaults.resetComponents(); final JRootPane rootPane = SwingUtilities.getRootPane(defaults.getContentPane()); SwingUtilities.updateComponentTreeUI(rootPane); // Use custom decorations when supported by the LAF final JFrame frame = (JFrame) SwingUtilities.windowForComponent(rootPane); frame.dispose(); if (UIManager.getLookAndFeel().getSupportsWindowDecorations()) { frame.setUndecorated(true); frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME); } else { frame.setUndecorated(false); } frame.setVisible(true); } catch (@NotNull final Exception ex) { ex.printStackTrace(); } } } /* * Close the frame */ private static final class ExitAction extends AbstractAction { private ExitAction() { putValue(Action.NAME, "Exit"); putValue(Action.SHORT_DESCRIPTION, getValue(Action.NAME)); putValue(Action.MNEMONIC_KEY, KeyEvent.VK_X); } public void actionPerformed(final ActionEvent e) { System.exit(0); } } }