All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pepsoft.worldpainter.MaterialSelector Maven / Gradle / Ivy

There is a newer version: 2.23.2
Show newest version
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.pepsoft.worldpainter;

import org.pepsoft.minecraft.Material;
import org.pepsoft.util.DesktopUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import javax.swing.border.TitledBorder;
import java.awt.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import static org.pepsoft.minecraft.Block.BLOCKS;
import static org.pepsoft.minecraft.Material.MINECRAFT;
import static org.pepsoft.util.AwtUtils.doLaterOnEventThread;
import static org.pepsoft.util.CollectionUtils.copyOf;
import static org.pepsoft.util.swing.MessageUtils.beepAndShowError;
import static org.pepsoft.worldpainter.Platform.Capability.NAME_BASED;

/**
 * A GUI control for selecting a Minecraft material. Supports both legacy
 * (pre-1.13) and modern (1.13 and later materials.
 *
 * @author Pepijn
 */
@SuppressWarnings({"unused", "FieldCanBeLocal"})
public class MaterialSelector extends javax.swing.JPanel {
    /**
     * Creates new form MaterialEditor
     */
    @SuppressWarnings("UseOfObsoleteCollectionType") // Tell that to Swing
    public MaterialSelector() {
        initComponents();
        
        Vector minecraftNames = new Vector<>(Material.getAllSimpleNamesForNamespace(Material.MINECRAFT));
        Collections.sort(minecraftNames);
        comboBoxMinecraftName.setModel(new DefaultComboBoxModel<>(minecraftNames));
        
        Vector namespaces = new Vector<>(Material.getAllNamespaces());
        Collections.sort(namespaces);
        comboBoxNamespace.setModel(new DefaultComboBoxModel<>(namespaces));

        String[] blockIds = new String[256];
        for (int i = 0; i < blockIds.length; i++) {
            if (BLOCKS[i].name != null) {
                blockIds[i] = i + ": " + BLOCKS[i].name;
            } else {
                blockIds[i] = Integer.toString(i);
            }
        }
        comboBoxBlockType.setModel(new DefaultComboBoxModel<>(blockIds));
    }
    
    public void setMaterial(Material material) {
        programmaticChange = true;
        try {
            this.material = material;
            namespace = material.namespace;
            simpleName = material.simpleName;
            loadActualProperties();
            if (legacyMode) {
                int blockType = material.blockType;
                if (blockType >= 0) {
                    comboBoxBlockType.setSelectedIndex(blockType);
                    spinnerDataValue.setValue(material.data);
                } else {
                    comboBoxBlockType.setSelectedItem(null);
                    spinnerDataValue.setValue(0);
                    doLaterOnEventThread(() -> beepAndShowError(this, "The material (" + material.name + ") is not compatible with the current map format (" + platform.displayName + ").\nSelect a compatible material.", "Incompatible Material"));
                }
                updateMaterialName();
            } else if (namespace.equals(MINECRAFT)) {
                radioButtonMinecraft.setSelected(true);
                comboBoxMinecraftName.setSelectedItem(simpleName);
            } else {
                radioButtonCustom.setSelected(true);
                comboBoxNamespace.setSelectedItem(namespace);
                updateKnownCustomNames();
                comboBoxCustomName.setSelectedItem(simpleName);
            }
            setControlStates();
        } finally {
            programmaticChange = false;
        }
    }
    
    public Material getMaterial() {
        updateMaterial();
        return material;
    }

    public boolean isExtendedBlockIds() {
        return extendedBlockIds;
    }

    public void setExtendedBlockIds(boolean extendedBlockIds) {
        if (extendedBlockIds != this.extendedBlockIds) {
            this.extendedBlockIds = extendedBlockIds;
            String[] blockIds = new String[extendedBlockIds ? 4096 : 256];
            for (int i = 0; i < blockIds.length; i++) {
                if (BLOCKS[i].name != null) {
                    blockIds[i] = i + ": " + BLOCKS[i].name;
                } else {
                    blockIds[i] = Integer.toString(i);
                }
            }
            comboBoxBlockType.setModel(new DefaultComboBoxModel<>(blockIds));
        }
    }

    public Platform getPlatform() {
        return platform;
    }

    public void setPlatform(Platform platform) {
        if (platform == null) {
            throw new NullPointerException();
        } else if (this.platform != null) {
            throw new IllegalStateException("Platform already set");
        }
        this.platform = platform;
        legacyMode = ! platform.capabilities.contains(NAME_BASED);
        if (legacyMode) {
            remove(0);
            ((TitledBorder) jScrollPane1.getBorder()).setTitle("Modern properties");
        } else {
            remove(1);
        }
        setControlStates();
    }

    /**
     * Load the properties of the current material into the properties panel.
     */
    private void loadActualProperties() {
        properties = copyOf(material.getProperties());
        updateProperties();
    }

    /**
     * Load the default properties (the properties of an arbitrary block with
     * the current namespace and simple name) into the properties panel.
     */
    private void loadDefaultProperties() {
        Material defaultMaterial = Material.getPrototype(namespace + ":" + simpleName);
        properties = copyOf(defaultMaterial.getProperties());
        updateProperties();
    }

    /**
     * Update the properties panel to reflect the current properties.
     */
    private void updateProperties() {
        boolean propertiesChanged = false;
        if (panelProperties.getComponentCount() > 0) {
            panelProperties.removeAll();
            propertyEditors.clear();
            propertiesChanged = true;
        }
        if (properties != null) {
            for (Map.Entry entry: properties.entrySet()) {
                final String name = entry.getKey();
                final String value = entry.getValue();
                final Material.PropertyDescriptor descriptor = (material.propertyDescriptors != null) ? material.propertyDescriptors.get(name) : null;
                if (descriptor != null) {
                    switch (descriptor.type) {
                        case BOOLEAN:
                            addBooleanProperty(name, Boolean.parseBoolean(value));
                            break;
                        case INTEGER:
                            addIntProperty(name, descriptor.minValue, Integer.parseInt(value), descriptor.maxValue);
                            break;
                        case ENUM:
                            addStringProperty(name, value, descriptor.enumValues, false);
                            break;
                        default:
                            throw new IllegalArgumentException("Unknown property type: " + descriptor.type);
                    }
                } else {
                    // No idea; just fall back to a string
                    addStringProperty(name, value, null, false);
                }
                propertiesChanged = true;
            }
        }
        if (propertiesChanged) {
            Window parentWindow = SwingUtilities.windowForComponent(this);
            if (parentWindow != null) {
                parentWindow.validate();
                repaint();
            }
        }
    }

    /**
     * Update the modern material name field to match the current material.
     */
    private void updateMaterialName() {
        labelMaterialName.setText(namespace + ":" + simpleName);
    }

    /**
     * Add a string-typed property to the properties panel.
     *
     * @param key The key of the property.
     * @param value The initial value of the property.
     * @param values Optionally, the possible values of the property.
     * @param focus Whether the new field should receive the keyboard focus.
     */
    private void addStringProperty(String key, String value, String[] values, boolean focus) {
        if (propertyEditors.containsKey(key)) {
            throw new IllegalStateException("Property " + key + " already present");
        } else if (focus && legacyMode) {
            throw new IllegalArgumentException("focus may not be true in legacy mode");
        }
        JComponent control;
        if (values != null) {
            control = new JComboBox<>(values);
            ((JComboBox) control).setSelectedItem(value);
        } else {
            control = new JTextField(value, 15);
        }
        control.setEnabled(! legacyMode);
        propertyEditors.put(key, control);
        if (! legacyMode) {
            if (control instanceof JComboBox) {
                ((JComboBox) control).addActionListener(e -> {
                    if (properties == null) {
                        properties = new HashMap<>();
                    }
                    properties.put(key, (String) ((JComboBox) control).getSelectedItem());
                    updateMaterial();
                });
            } else {
                control.addFocusListener(new FocusAdapter() {
                    @Override
                    public void focusLost(FocusEvent e) {
                        if (properties == null) {
                            properties = new HashMap<>();
                        }
                        properties.put(key, ((JTextField) control).getText());
                        updateMaterial();
                    }
                });
            }
        }
        JLabel label = new JLabel(uppercaseFirst(key) + ':');
        label.setLabelFor(control);
        addControlsRow(label, control);
        if (focus) {
            control.requestFocusInWindow();
        }
    }

    /**
     * Add a integer-typed property to the properties panel.
     *
     * @param key      The key of the property.
     * @param minValue The minimum value of the property.
     * @param value    The initial value of the property.
     * @param maxValue The maximum value of the property.
     */
    private void addIntProperty(String key, int minValue, int value, int maxValue) {
        if (propertyEditors.containsKey(key)) {
            throw new IllegalStateException("Property " + key + " already present");
        }
        JSpinner control = new JSpinner(new SpinnerNumberModel(value, minValue, maxValue, 1));
        control.setEnabled(! legacyMode);
        propertyEditors.put(key, control);
        if (! legacyMode) {
            control.addChangeListener(e -> {
                if (properties == null) {
                    properties = new HashMap<>();
                }
                properties.put(key, Integer.toString((Integer) control.getValue()));
                updateMaterial();
            });
        }
        JLabel label = new JLabel(uppercaseFirst(key) + ':');
        label.setLabelFor(control);
        addControlsRow(label, control); // TODO: determine actual bounds, if any
    }

    /**
     * Add a boolean-typed property to the properties panel.
     *
     * @param key   The key of the property.
     * @param value The initial value of the property.
     */
    private void addBooleanProperty(String key, boolean value) {
        if (propertyEditors.containsKey(key)) {
            throw new IllegalStateException("Property " + key + " already present");
        }
        // Use a zero width space as the text so that the checkbox aligns to the
        // base line of the label
        JCheckBox control = new JCheckBox("\u200b", value);
        control.setEnabled(! legacyMode);
        propertyEditors.put(key, control);
        if (! legacyMode) {
            control.addActionListener(e -> {
                if (properties == null) {
                    properties = new HashMap<>();
                }
                properties.put(key, Boolean.toString(control.isSelected()));
                updateMaterial();
            });
        }
        JLabel label = new JLabel(uppercaseFirst(key) + ':');
        label.setLabelFor(control);
        addControlsRow(label, control);
    }

    /**
     * Change the first letter of a string to uppercase.
     *
     * @param str The string to change.
     * @return The string with its first letter changed to uppercase.
     */
    private String uppercaseFirst(String str) {
        return str.isEmpty() ? str : Character.toUpperCase(str.charAt(0)) + str.substring(1);
    }

    /**
     * Add a left-adjusted row of AWT controls to the properties panel.
     *
     * @param controls The controls to add.
     */
    private void addControlsRow(Component... controls) {
        GridBagConstraints constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.BASELINE_LEADING;
        constraints.insets = new Insets(2, 2, 0, 0);
        for (Component control: controls) {
            panelProperties.add(control, constraints);
        }
        constraints.gridwidth = GridBagConstraints.REMAINDER;
        constraints.weightx = 1.0;
        panelProperties.add(Box.createGlue(), constraints);
    }

    /**
     * Interactively add a property to {@link #properties} and the properties
     * panel.
     */
    private void addProperty() {
        final String name = JOptionPane.showInputDialog(this, "Enter the name of the property to add:", "Add Property", JOptionPane.QUESTION_MESSAGE);
        if ((name != null) && (! name.trim().isEmpty())) {
            addStringProperty(name.trim(), "", null, true);
            SwingUtilities.windowForComponent(this).validate();
            repaint();
        }
    }

    /**
     * Make sure all controls are correctly enabled or disabled according to
     * their current settings.
     */
    private void setControlStates() {
        if (! legacyMode) {
            boolean minecraft = radioButtonMinecraft.isSelected();
            comboBoxMinecraftName.setEnabled(minecraft);
            comboBoxNamespace.setEnabled(! minecraft);
            comboBoxCustomName.setEnabled(! minecraft);
            buttonAddProperty.setEnabled(! minecraft);
        }
    }

    /**
     * Update the current material to a legacy material based on the currently
     * selected block ID and data value.
     */
    private void blockIdOrDataChanged() {
        int blockType = comboBoxBlockType.getSelectedIndex();
        int dataValue = (Integer) spinnerDataValue.getValue();
        if ((blockType < 0) || (blockType > 4095) || (dataValue < 0) || (dataValue > 15)) {
            // No idea why this happens, but it has been observed in the wild TODO find out why and fix the underlying
            //  cause
            logger.error("blockIdOrDataChanged(): blockType = {}, dataValue = {}, comboBoxMinecraftName.selectedItem = {}, comboBoxNamespace.selectedItem = {}, comboBoxCustomName.selectedItem = {}", blockType, dataValue, comboBoxMinecraftName.getSelectedItem(), comboBoxNamespace.getSelectedItem(), comboBoxCustomName.getSelectedItem());
            return;
        }
        material = Material.get(blockType, dataValue);
        namespace = material.namespace;
        simpleName = material.simpleName;
        loadActualProperties();
        updateMaterialName();
        firePropertyChange("material", null, getMaterial());
    }

    /**
     * Update the current material to a modern material based on the Minecraft
     * namespace currently
     * selected namespace and simple name.
     */
    private void minecraftNameChanged() {
        namespace = Material.MINECRAFT;
        simpleName = (String) comboBoxMinecraftName.getSelectedItem();
        material = Material.getPrototype(namespace + ':' + simpleName);
        loadDefaultProperties();
        firePropertyChange("material", null, getMaterial());
    }
    
    private void updateMaterial() {
        final Material oldMaterial = material;
        final boolean previousProgrammaticChange = programmaticChange;
        programmaticChange = true;
        try {
            if (legacyMode) {
                if (comboBoxBlockType.getSelectedItem() != null) {
                    material = Material.get(comboBoxBlockType.getSelectedIndex(), (int) spinnerDataValue.getValue());
                }
            } else {
                if (radioButtonCustom.isSelected()) {
                    // Make sure to finish editing the custom name, even if the field still has the keyboard focus
                    simpleName = (String) comboBoxCustomName.getSelectedItem();
                } else {
                    simpleName = (String) comboBoxMinecraftName.getSelectedItem();
                }
                if ((simpleName != null) && (! simpleName.trim().isEmpty())) {
                    if ((namespace == null) || namespace.trim().isEmpty()) {
                        namespace = Material.MINECRAFT;
                    }
                    material = Material.get(namespace.trim() + ':' + simpleName.trim(), properties);
                }
            }
            if (material != oldMaterial) {
                firePropertyChange("material", null, material);
            }
        } finally {
            programmaticChange = previousProgrammaticChange;
        }
    }

    private void updateKnownCustomNames() {
        final Vector simpleNames;
        if (MINECRAFT.equals(namespace)) {
            simpleNames = new Vector<>();
        } else {
            simpleNames = new Vector<>(Material.getAllSimpleNamesForNamespace(namespace));
            Collections.sort(simpleNames);
        }
        comboBoxCustomName.setModel(new DefaultComboBoxModel<>(simpleNames));
        comboBoxCustomName.setSelectedItem(simpleName);
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings({"Convert2Lambda", "Anonymous2MethodRef", "DataFlowIssue"})
    // //GEN-BEGIN:initComponents
    private void initComponents() {

        buttonGroup1 = new javax.swing.ButtonGroup();
        jPanel1 = new javax.swing.JPanel();
        radioButtonCustom = new javax.swing.JRadioButton();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        comboBoxMinecraftName = new javax.swing.JComboBox<>();
        comboBoxNamespace = new javax.swing.JComboBox<>();
        radioButtonMinecraft = new javax.swing.JRadioButton();
        comboBoxCustomName = new javax.swing.JComboBox<>();
        jPanel2 = new javax.swing.JPanel();
        jLabel3 = new javax.swing.JLabel();
        comboBoxBlockType = new javax.swing.JComboBox<>();
        jLabel4 = new javax.swing.JLabel();
        spinnerDataValue = new javax.swing.JSpinner();
        jLabel5 = new javax.swing.JLabel();
        jLabel6 = new javax.swing.JLabel();
        labelMaterialName = new javax.swing.JLabel();
        jPanel3 = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        panelProperties = new javax.swing.JPanel();
        buttonAddProperty = new javax.swing.JButton();

        setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.PAGE_AXIS));

        buttonGroup1.add(radioButtonCustom);
        radioButtonCustom.setText("Custom:");
        radioButtonCustom.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                radioButtonCustomActionPerformed(evt);
            }
        });

        jLabel1.setText(":");

        jLabel2.setText("minecraft:");

        comboBoxMinecraftName.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
        comboBoxMinecraftName.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                comboBoxMinecraftNameActionPerformed(evt);
            }
        });

        comboBoxNamespace.setEditable(true);
        comboBoxNamespace.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
        comboBoxNamespace.setEnabled(false);
        comboBoxNamespace.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                comboBoxNamespaceActionPerformed(evt);
            }
        });

        buttonGroup1.add(radioButtonMinecraft);
        radioButtonMinecraft.setSelected(true);
        radioButtonMinecraft.setText("Minecraft:");
        radioButtonMinecraft.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                radioButtonMinecraftActionPerformed(evt);
            }
        });

        comboBoxCustomName.setEditable(true);
        comboBoxCustomName.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusLost(java.awt.event.FocusEvent evt) {
                comboBoxCustomNameFocusLost(evt);
            }
        });
        comboBoxCustomName.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                comboBoxCustomNameActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(radioButtonCustom, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addComponent(radioButtonMinecraft, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jLabel2)
                        .addGap(0, 0, 0)
                        .addComponent(comboBoxMinecraftName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addGap(21, 21, 21)
                        .addComponent(comboBoxNamespace, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jLabel1)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(comboBoxCustomName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap(62, Short.MAX_VALUE))
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(radioButtonMinecraft, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel2)
                    .addComponent(comboBoxMinecraftName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(radioButtonCustom, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(comboBoxNamespace, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(comboBoxCustomName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        add(jPanel1);

        jLabel3.setText("Block ID:");

        comboBoxBlockType.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                comboBoxBlockTypeActionPerformed(evt);
            }
        });

        jLabel4.setText("Data value:");

        spinnerDataValue.setModel(new javax.swing.SpinnerNumberModel(0, 0, 15, 1));
        spinnerDataValue.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                spinnerDataValueStateChanged(evt);
            }
        });

        jLabel5.setForeground(new java.awt.Color(0, 0, 255));
        jLabel5.setText("look up legacy block IDs and data values");
        jLabel5.setVerticalAlignment(javax.swing.SwingConstants.TOP);
        jLabel5.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
        jLabel5.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jLabel5MouseClicked(evt);
            }
        });

        jLabel6.setText("Modern name:");

        labelMaterialName.setText("minecraft:grass_block");

        javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jLabel5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addGroup(jPanel2Layout.createSequentialGroup()
                        .addComponent(jLabel3)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(comboBoxBlockType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(18, 18, 18)
                        .addComponent(jLabel4)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(spinnerDataValue, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(jPanel2Layout.createSequentialGroup()
                        .addComponent(jLabel6)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(labelMaterialName)))
                .addContainerGap(135, Short.MAX_VALUE))
        );
        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel3)
                    .addComponent(comboBoxBlockType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel4)
                    .addComponent(spinnerDataValue, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(18, 18, 18)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel6)
                    .addComponent(labelMaterialName))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        add(jPanel2);

        jScrollPane1.setBorder(javax.swing.BorderFactory.createTitledBorder("Properties"));

        panelProperties.setLayout(new java.awt.GridBagLayout());
        jScrollPane1.setViewportView(panelProperties);

        buttonAddProperty.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/pepsoft/worldpainter/icons/brick_add.png"))); // NOI18N
        buttonAddProperty.setToolTipText("Add a property");
        buttonAddProperty.setEnabled(false);
        buttonAddProperty.setMargin(new java.awt.Insets(2, 2, 2, 2));
        buttonAddProperty.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                buttonAddPropertyActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
        jPanel3.setLayout(jPanel3Layout);
        jPanel3Layout.setHorizontalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel3Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 307, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(buttonAddProperty)
                .addContainerGap())
        );
        jPanel3Layout.setVerticalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel3Layout.createSequentialGroup()
                .addGap(0, 0, 0)
                .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel3Layout.createSequentialGroup()
                        .addComponent(buttonAddProperty)
                        .addGap(0, 62, Short.MAX_VALUE))
                    .addComponent(jScrollPane1))
                .addContainerGap())
        );

        add(jPanel3);
    }// //GEN-END:initComponents

    private void radioButtonCustomActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_radioButtonCustomActionPerformed
        namespace = (String) comboBoxNamespace.getSelectedItem();
        simpleName = (String) comboBoxCustomName.getSelectedItem();
        updateKnownCustomNames();
        setControlStates();
        properties = null;
        updateProperties();
        updateMaterial();
    }//GEN-LAST:event_radioButtonCustomActionPerformed

    private void comboBoxNamespaceActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comboBoxNamespaceActionPerformed
        if (! programmaticChange) {
            programmaticChange = true;
            try {
                namespace = (String) comboBoxNamespace.getSelectedItem();
                updateKnownCustomNames();
                updateMaterial();
            } finally {
                programmaticChange = false;
            }
        }
    }//GEN-LAST:event_comboBoxNamespaceActionPerformed

    private void comboBoxBlockTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comboBoxBlockTypeActionPerformed
        if (! programmaticChange) {
            programmaticChange = true;
            try {
                spinnerDataValue.setValue(0);
                blockIdOrDataChanged();
            } finally {
                programmaticChange = false;
            }
        }
    }//GEN-LAST:event_comboBoxBlockTypeActionPerformed

    private void spinnerDataValueStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spinnerDataValueStateChanged
        if (! programmaticChange) {
            programmaticChange = true;
            try {
                blockIdOrDataChanged();
            } finally {
                programmaticChange = false;
            }
        }
    }//GEN-LAST:event_spinnerDataValueStateChanged

    private void radioButtonMinecraftActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_radioButtonMinecraftActionPerformed
        namespace = Material.MINECRAFT;
        updateMaterial();
        setControlStates();
        loadDefaultProperties();
        firePropertyChange("material", null, getMaterial());
    }//GEN-LAST:event_radioButtonMinecraftActionPerformed

    private void jLabel5MouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jLabel5MouseClicked
        try {
            DesktopUtils.open(new URL("https://www.worldpainter.net/links/dataValues"));
        } catch (MalformedURLException e) {
            throw new RuntimeException("Malformed URL exception while trying to open https://www.worldpainter.net/links/dataValues", e);
        }
    }//GEN-LAST:event_jLabel5MouseClicked

    private void comboBoxMinecraftNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comboBoxMinecraftNameActionPerformed
        if (! programmaticChange) {
            programmaticChange = true;
            try {
                minecraftNameChanged();
            } finally {
                programmaticChange = false;
            }
        }
    }//GEN-LAST:event_comboBoxMinecraftNameActionPerformed

    private void comboBoxCustomNameFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_comboBoxCustomNameFocusLost
        simpleName = (String) comboBoxCustomName.getSelectedItem();
        programmaticChange = true;
        try {
            updateMaterial();
        } finally {
            programmaticChange = false;
        }
    }//GEN-LAST:event_comboBoxCustomNameFocusLost

    private void comboBoxCustomNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comboBoxCustomNameActionPerformed
        if (! programmaticChange) {
            simpleName = (String) comboBoxCustomName.getSelectedItem();
            programmaticChange = true;
            try {
                updateMaterial();
            } finally {
                programmaticChange = false;
            }
        }
    }//GEN-LAST:event_comboBoxCustomNameActionPerformed

    private void buttonAddPropertyActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonAddPropertyActionPerformed
        addProperty();
    }//GEN-LAST:event_buttonAddPropertyActionPerformed


    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton buttonAddProperty;
    private javax.swing.ButtonGroup buttonGroup1;
    private javax.swing.JComboBox comboBoxBlockType;
    private javax.swing.JComboBox comboBoxCustomName;
    private javax.swing.JComboBox comboBoxMinecraftName;
    private javax.swing.JComboBox comboBoxNamespace;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JLabel labelMaterialName;
    private javax.swing.JPanel panelProperties;
    private javax.swing.JRadioButton radioButtonCustom;
    private javax.swing.JRadioButton radioButtonMinecraft;
    private javax.swing.JSpinner spinnerDataValue;
    // End of variables declaration//GEN-END:variables

    private final Map propertyEditors = new HashMap<>();
    private boolean extendedBlockIds, programmaticChange, legacyMode;
    private Platform platform;
    private Material material;
    private String namespace, simpleName;
    private Map properties;

    private static final Logger logger = LoggerFactory.getLogger(MaterialSelector.class);
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy