/* * 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.layers.bo2; import org.pepsoft.minecraft.Constants; import org.pepsoft.minecraft.MCInterface; import org.pepsoft.util.DesktopUtils; import org.pepsoft.worldpainter.BiomeScheme; import org.pepsoft.worldpainter.ColourScheme; import org.pepsoft.worldpainter.Configuration; import org.pepsoft.worldpainter.biomeschemes.BiomeSchemeManager; import org.pepsoft.worldpainter.layers.AbstractLayerEditor; import org.pepsoft.worldpainter.layers.Bo2Layer; import org.pepsoft.worldpainter.layers.exporters.ExporterSettings; import org.pepsoft.worldpainter.objects.WPObject; import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.filechooser.FileFilter; import javax.vecmath.Point3i; import java.awt.*; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.net.MalformedURLException; import java.net.URL; import java.text.NumberFormat; import java.util.*; import java.util.List; import static org.pepsoft.worldpainter.Constants.*; import static org.pepsoft.worldpainter.objects.WPObject.*; /** * * @author Pepijn Schmitz */ public class Bo2LayerEditor extends AbstractLayerEditor<Bo2Layer> implements ListSelectionListener, DocumentListener { /** * Creates new form Bo2LayerEditor */ public Bo2LayerEditor() { initComponents(); listModel = new DefaultListModel(); listObjects.setModel(listModel); listObjects.setCellRenderer(new WPObjectListCellRenderer()); listObjects.getSelectionModel().addListSelectionListener(this); fieldName.getDocument().addDocumentListener(this); updateBlocksPerAttempt(); } // LayerEditor @Override public Bo2Layer createLayer() { return new Bo2Layer(new Bo2ObjectTube("My Custom Objects", Collections.emptyList()), "Custom bo2, bo3 and/or schematic objects", Color.ORANGE.getRGB()); } @Override public void setLayer(Bo2Layer layer) { super.setLayer(layer); reset(); } @Override public void commit() { if (! isCommitAvailable()) { throw new IllegalStateException("Settings invalid or incomplete"); } saveSettings(layer); } @Override public void reset() { List<WPObject> objects = new ArrayList<>(); fieldName.setText(layer.getName()); selectedColour = layer.getColour(); List<File> files = layer.getFiles(); if (files != null) { if (files.isEmpty()) { if (logger.isDebugEnabled()) { logger.debug("Existing layer contains new style objects"); } // New layer; files stored in object attributes objects.addAll(layer.getObjectProvider().getAllObjects()); } else { // Old layer; files stored separately int missingFiles = 0; if ((files.size() == 1) && files.get(0).isDirectory()) { logger.info("Existing custom object layer contains old style directory; migrating to new style"); File[] filesInDir = files.get(0).listFiles((dir, name) -> name.toLowerCase().endsWith(".bo2") || name.toLowerCase().endsWith(".schematic")); //noinspection ConstantConditions // Cannot happen as we already checked that files.get(0) is an extant directory for (File file: filesInDir) { try { WPObject object; if (file.getName().toLowerCase().endsWith(".bo2")) { object = Bo2Object.load(file); } else { object = Schematic.load(file); } objects.add(object); } catch (IOException e) { logger.error("I/O error while trying to load custom object " + file, e); missingFiles++; } } } else { logger.info("Existing custom object layer contains old style file list; migrating to new style"); for (File file: files) { if (file.exists()) { try { WPObject object; if (file.getName().toLowerCase().endsWith(".bo2")) { object = Bo2Object.load(file); } else { object = Schematic.load(file); } objects.add(object); } catch (IOException e) { logger.error("I/O error while trying to load custom object " + file, e); missingFiles++; } } else { missingFiles++; } } } if (missingFiles > 0) { JOptionPane.showMessageDialog(this, "This is an old custom object layer and " + missingFiles + " objects\ncould NOT be restored because they were missing or\nreading them resulted in an I/O error.\n\nYou will have to re-add these objects before\nsaving the settings, otherwise the existing object\ndata will be gone. You may also cancel the dialog\nwithout affecting the object data.", "Missing Files", JOptionPane.WARNING_MESSAGE); } } } else { logger.info("Existing custom object layer contains very old style objects with no file information; migrating to new style"); // Very old layer; no file information at all objects.addAll(layer.getObjectProvider().getAllObjects()); } listModel.clear(); for (WPObject object: objects) { listModel.addElement(object.clone()); } spinnerBlocksPerAttempt.setValue(layer.getDensity()); setLabelColour(); refreshLeafDecaySettings(); settingsChanged(); } @Override public ExporterSettings getSettings() { if (! isCommitAvailable()) { throw new IllegalStateException("Settings invalid or incomplete"); } final Bo2Layer previewLayer = saveSettings(null); return new ExporterSettings() { @Override public boolean isApplyEverywhere() { return false; } @Override public Bo2Layer getLayer() { return previewLayer; } @Override public ExporterSettings clone() { throw new UnsupportedOperationException("Not supported"); } }; } @Override public boolean isCommitAvailable() { boolean filesSelected = listModel.getSize() > 0; boolean nameSpecified = fieldName.getText().trim().length() > 0; return filesSelected && nameSpecified; } @Override public void setContext(LayerEditorContext context) { super.setContext(context); colourScheme = context.getColourScheme(); } // ListSelectionListener @Override public void valueChanged(ListSelectionEvent e) { settingsChanged(); } // DocumentListener @Override public void insertUpdate(DocumentEvent e) { settingsChanged(); } @Override public void removeUpdate(DocumentEvent e) { settingsChanged(); } @Override public void changedUpdate(DocumentEvent e) { settingsChanged(); } private Bo2Layer saveSettings(Bo2Layer layer) { String name = fieldName.getText(); List<WPObject> objects = new ArrayList<>(listModel.getSize()); for (int i = 0; i < listModel.getSize(); i++) { objects.add((WPObject) listModel.getElementAt(i)); } Bo2ObjectProvider objectProvider = new Bo2ObjectTube(name, objects); if (layer == null) { layer = new Bo2Layer(objectProvider, "Custom bo2, bo3 and/or schematic objects", selectedColour); } else { layer.setObjectProvider(objectProvider); layer.setColour(selectedColour); } layer.setDensity((Integer) spinnerBlocksPerAttempt.getValue()); return layer; } private void pickColour() { Color pick = JColorChooser.showDialog(this, "Select Colour", new Color(selectedColour)); if (pick != null) { selectedColour = pick.getRGB(); setLabelColour(); } } private void setLabelColour() { jLabel5.setBackground(new Color(selectedColour)); } private void settingsChanged() { setControlStates(); context.settingsChanged(); } private void setControlStates() { boolean filesSelected = listModel.getSize() > 0; boolean objectsSelected = listObjects.getSelectedIndex() != -1; buttonRemoveFile.setEnabled(objectsSelected); buttonReloadAll.setEnabled(filesSelected); buttonEdit.setEnabled(objectsSelected); } private void addFilesOrDirectory() { // Can't use FileUtils.selectFilesForOpen() because it doesn't support // selecting directories, or adding custom components to the dialog JFileChooser fileChooser = new JFileChooser(); Configuration config = Configuration.getInstance(); if ((config.getCustomObjectsDirectory() != null) && config.getCustomObjectsDirectory().isDirectory()) { fileChooser.setCurrentDirectory(config.getCustomObjectsDirectory()); } fileChooser.setDialogTitle("Select File(s) or Directory"); fileChooser.setMultiSelectionEnabled(true); fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); fileChooser.setFileFilter(new FileFilter() { @Override public boolean accept(File f) { return f.isDirectory() || f.getName().toLowerCase().endsWith(".bo2") || f.getName().toLowerCase().endsWith(".bo3") || f.getName().toLowerCase().endsWith(".schematic") || f.getName().toLowerCase().endsWith(".nbt"); } @Override public String getDescription() { return "Custom Object Files (*.bo2, *.bo3, *.schematic, *.nbt)"; } }); WPObjectPreviewer previewer = new WPObjectPreviewer(); fileChooser.addPropertyChangeListener(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY, previewer); fileChooser.setAccessory(previewer); if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { File[] selectedFiles = fileChooser.getSelectedFiles(); if (selectedFiles.length > 0) { config.setCustomObjectsDirectory(selectedFiles[0].getParentFile()); for (File selectedFile: selectedFiles) { if (selectedFile.isDirectory()) { if (fieldName.getText().isEmpty()) { String name = selectedFiles[0].getName(); if (name.length() > 12) { name = "..." + name.substring(name.length() - 10); } fieldName.setText(name); } File[] files = selectedFile.listFiles((dir, name) -> name.toLowerCase().endsWith(".bo2") || name.toLowerCase().endsWith(".bo3") || name.toLowerCase().endsWith(".schematic") || name.toLowerCase().endsWith(".nbt")); //noinspection ConstantConditions // Cannot happen as we already checked selectedFile is an extant directory if (files.length == 0) { JOptionPane.showMessageDialog(this, "Directory " + selectedFile.getName() + " does not contain any .bo2, .bo3, .schematic or .nbt files.", "No Custom Object Files", JOptionPane.ERROR_MESSAGE); } else { MCInterface mcInterface = null; for (File file: files) { try { WPObject object; if (file.getName().toLowerCase().endsWith(".bo2")) { object = Bo2Object.load(file); } else if (file.getName().toLowerCase().endsWith(".bo3")) { object = Bo3Object.load(file); } else if (file.getName().toLowerCase().endsWith(".nbt")) { if (mcInterface == null) { BiomeScheme biomeScheme = BiomeSchemeManager.getSharedBiomeScheme(BIOME_ALGORITHM_1_7_DEFAULT); if (biomeScheme instanceof MCInterface) { mcInterface = (MCInterface) biomeScheme; } else { throw new RuntimeException("WorldPainter requires access to Minecraft 1.10.2 or newer for loading *.nbt files"); } } object = Structure.load(file, mcInterface); } else { object = Schematic.load(file); } listModel.addElement(object); } catch (IOException e) { logger.error("I/O error while trying to load custom object " + file, e); JOptionPane.showMessageDialog(this, "I/O error while loading " + file.getName() + "; it was not added", "I/O Error", JOptionPane.ERROR_MESSAGE); } } } } else { if (fieldName.getText().isEmpty()) { String name = selectedFile.getName(); int p = name.lastIndexOf('.'); if (p != -1) { name = name.substring(0, p); } if (name.length() > 12) { name = "..." + name.substring(name.length() - 10); } fieldName.setText(name); } try { WPObject object; if (selectedFile.getName().toLowerCase().endsWith(".bo2")) { object = Bo2Object.load(selectedFile); } else if (selectedFile.getName().toLowerCase().endsWith(".bo3")) { object = Bo3Object.load(selectedFile); } else if (selectedFile.getName().toLowerCase().endsWith(".nbt")) { BiomeScheme biomeScheme = BiomeSchemeManager.getSharedBiomeScheme(BIOME_ALGORITHM_1_7_DEFAULT); if (biomeScheme instanceof MCInterface) { object = Structure.load(selectedFile, (MCInterface) biomeScheme); } else { throw new RuntimeException("WorldPainter requires access to Minecraft 1.10.2 or newer for loading *.nbt files"); } } else { object = Schematic.load(selectedFile); } listModel.addElement(object); } catch (IOException e) { logger.error("I/O error while trying to load custom object " + selectedFile, e); JOptionPane.showMessageDialog(this, "I/O error while loading " + selectedFile.getName() + "; it was not added", "I/O Error", JOptionPane.ERROR_MESSAGE); } } } settingsChanged(); refreshLeafDecaySettings(); } } } private void removeFiles() { int[] selectedIndices = listObjects.getSelectedIndices(); for (int i = selectedIndices.length - 1; i >= 0; i--) { listModel.removeElementAt(selectedIndices[i]); } settingsChanged(); refreshLeafDecaySettings(); } private void reloadObjects() { StringBuilder noFiles = new StringBuilder(); StringBuilder notFound = new StringBuilder(); StringBuilder errors = new StringBuilder(); int[] indices; if (listObjects.getSelectedIndex() != -1) { indices = listObjects.getSelectedIndices(); } else { indices = new int[listModel.getSize()]; for (int i = 0; i < indices.length; i++) { indices[i] = i; } } for (int indice : indices) { WPObject object = (WPObject) listModel.getElementAt(indice); File file = object.getAttribute(ATTRIBUTE_FILE); if (file != null) { if (file.isFile() && file.canRead()) { try { Map<String, Serializable> existingAttributes = object.getAttributes(); if (file.getName().toLowerCase().endsWith(".bo2")) { object = Bo2Object.load(file); } else if (file.getName().toLowerCase().endsWith(".bo3")) { object = Bo3Object.load(file); } else { object = Schematic.load(file); } if (existingAttributes != null) { Map<String, Serializable> attributes = object.getAttributes(); if (attributes == null) { attributes = new HashMap<>(); } attributes.putAll(existingAttributes); object.setAttributes(attributes); } listModel.setElementAt(object, indice); } catch (IOException e) { logger.error("I/O error while reloading " + file, e); errors.append(file.getPath()).append('\n'); } } else { notFound.append(file.getPath()).append('\n'); } } else { noFiles.append(object.getName()).append('\n'); } } if ((noFiles.length() > 0) || (notFound.length() > 0)) { StringBuilder message = new StringBuilder(); message.append("Not all files could be reloaded!\n"); if (noFiles.length() > 0) { message.append("\nThe following objects came from an old layer and have no filename stored:\n"); message.append(noFiles); } if (notFound.length() > 0) { message.append("\nThe following files were missing or not accessible:\n"); message.append(notFound); } JOptionPane.showMessageDialog(this, message, "Not All Files Reloaded", JOptionPane.ERROR_MESSAGE); } else { JOptionPane.showMessageDialog(this, indices.length + " objects successfully reloaded", "Success", JOptionPane.INFORMATION_MESSAGE); } refreshLeafDecaySettings(); } private void editObjects() { List<WPObject> selectedObjects = new ArrayList<>(listObjects.getSelectedIndices().length); int[] selectedIndices = listObjects.getSelectedIndices(); for (int i = selectedIndices.length - 1; i >= 0; i--) { selectedObjects.add((WPObject) listModel.getElementAt(selectedIndices[i])); } EditObjectAttributes dialog = new EditObjectAttributes(SwingUtilities.getWindowAncestor(this), selectedObjects, colourScheme); dialog.setVisible(true); if (! dialog.isCancelled()) { refreshLeafDecaySettings(); } } private void refreshLeafDecaySettings() { if (listModel.isEmpty()) { labelLeafDecayTitle.setEnabled(false); labelEffectiveLeafDecaySetting.setEnabled(false); labelEffectiveLeafDecaySetting.setText("N/A"); buttonSetDecay.setEnabled(false); buttonSetNoDecay.setEnabled(false); buttonReset.setEnabled(false); return; } boolean decayingLeavesFound = false; boolean nonDecayingLeavesFound = false; outer: for (Enumeration<WPObject> e = (Enumeration<WPObject>) listModel.elements(); e.hasMoreElements(); ) { WPObject object = e.nextElement(); int leafDecayMode = object.getAttribute(ATTRIBUTE_LEAF_DECAY_MODE); switch (leafDecayMode) { case LEAF_DECAY_NO_CHANGE: // Leaf decay attribute not set (or set to "no change"); // examine actual blocks Point3i dim = object.getDimensions(); for (int x = 0; x < dim.x; x++) { for (int y = 0; y < dim.y; y++) { for (int z = 0; z < dim.z; z++) { if ((object.getMask(x, y, z)) && ((object.getMaterial(x, y, z).blockType == Constants.BLK_LEAVES) || (object.getMaterial(x, y, z).blockType == Constants.BLK_LEAVES2))) { if ((object.getMaterial(x, y, z).data & 0x4) == 0x4) { // Non decaying leaf block nonDecayingLeavesFound = true; if (decayingLeavesFound) { // We have enough information; no // reason to continue the // examination break outer; } } else { // Decaying leaf block decayingLeavesFound = true; if (nonDecayingLeavesFound) { // We have enough information; no // reason to continue the // examination break outer; } } } } } } break; case LEAF_DECAY_OFF: // Leaf decay attribute set to "off"; don't examine blocks // for performance (even though this could lead to // misleading information if the object doesn't contain any // leaf blocks) nonDecayingLeavesFound = true; if (decayingLeavesFound) { // We have enough information; no reason to continue the // examination break outer; } break; case LEAF_DECAY_ON: // Leaf decay attribute set to "off"; don't examine blocks // for performance (even though this could lead to // misleading information if the object doesn't contain any // leaf blocks) decayingLeavesFound = true; if (nonDecayingLeavesFound) { // We have enough information; no reason to continue the // examination break outer; } break; default: throw new InternalError(); } } if (decayingLeavesFound) { if (nonDecayingLeavesFound) { // Both decaying and non decaying leaves found labelLeafDecayTitle.setEnabled(true); labelEffectiveLeafDecaySetting.setEnabled(true); labelEffectiveLeafDecaySetting.setText("<html>Decaying <i>and</i> non decaying leaves.</html>"); buttonSetDecay.setEnabled(true); buttonSetNoDecay.setEnabled(true); buttonReset.setEnabled(true); } else { // Only decaying leaves found labelLeafDecayTitle.setEnabled(true); labelEffectiveLeafDecaySetting.setEnabled(true); labelEffectiveLeafDecaySetting.setText("<html>Leaves <b>do</b> decay.</html>"); buttonSetDecay.setEnabled(false); buttonSetNoDecay.setEnabled(true); buttonReset.setEnabled(true); } } else { if (nonDecayingLeavesFound) { // Only non decaying leaves found labelLeafDecayTitle.setEnabled(true); labelEffectiveLeafDecaySetting.setEnabled(true); labelEffectiveLeafDecaySetting.setText("<html>Leaves do <b>not</b> decay.</html>"); buttonSetDecay.setEnabled(true); buttonSetNoDecay.setEnabled(false); buttonReset.setEnabled(true); } else { // No leaf blocks encountered at all, so N/A labelLeafDecayTitle.setEnabled(false); labelEffectiveLeafDecaySetting.setEnabled(false); labelEffectiveLeafDecaySetting.setText("N/A"); buttonSetDecay.setEnabled(false); buttonSetNoDecay.setEnabled(false); buttonReset.setEnabled(false); } } } private void setLeavesDecay() { for (Enumeration<WPObject> e = (Enumeration<WPObject>) listModel.elements(); e.hasMoreElements(); ) { WPObject object = e.nextElement(); object.setAttribute(ATTRIBUTE_LEAF_DECAY_MODE, LEAF_DECAY_ON); } refreshLeafDecaySettings(); } private void setLeavesNoDecay() { for (Enumeration<WPObject> e = (Enumeration<WPObject>) listModel.elements(); e.hasMoreElements(); ) { WPObject object = e.nextElement(); object.setAttribute(ATTRIBUTE_LEAF_DECAY_MODE, LEAF_DECAY_OFF); } refreshLeafDecaySettings(); } private void resetLeafDecay() { for (Enumeration<WPObject> e = (Enumeration<WPObject>) listModel.elements(); e.hasMoreElements(); ) { WPObject object = e.nextElement(); object.getAttributes().remove(ATTRIBUTE_LEAF_DECAY_MODE); } refreshLeafDecaySettings(); } private void updateBlocksPerAttempt() { int blocksAt50 = (Integer) spinnerBlocksPerAttempt.getValue(); int blocksAt1 = blocksAt50 * 64, blocksAt100 = (int) (blocksAt50 / 3.515625f + 0.5f); StringBuilder sb = new StringBuilder(); sb.append("one per ").append(numberFormat.format(blocksAt1)).append(" blocks at 1%"); if (blocksAt100 <= 1) { sb.append("; one every block at 100%)"); } else { sb.append("; one per ").append(numberFormat.format(blocksAt100)).append(" blocks at 100%)"); } labelBlocksPerAttempt.setText(sb.toString()); } /** * 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("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { buttonReloadAll = new javax.swing.JButton(); jSeparator2 = new javax.swing.JSeparator(); buttonEdit = new javax.swing.JButton(); jPanel3 = new javax.swing.JPanel(); labelLeafDecayTitle = new javax.swing.JLabel(); labelEffectiveLeafDecaySetting = new javax.swing.JLabel(); buttonSetDecay = new javax.swing.JButton(); buttonSetNoDecay = new javax.swing.JButton(); buttonReset = new javax.swing.JButton(); jScrollPane1 = new javax.swing.JScrollPane(); listObjects = new javax.swing.JList(); jLabel6 = new javax.swing.JLabel(); jLabel1 = new javax.swing.JLabel(); jPanel2 = new javax.swing.JPanel(); jLabel3 = new javax.swing.JLabel(); fieldName = new javax.swing.JTextField(); jLabel4 = new javax.swing.JLabel(); jLabel5 = new javax.swing.JLabel(); buttonPickColour = new javax.swing.JButton(); jLabel2 = new javax.swing.JLabel(); buttonAddFile = new javax.swing.JButton(); buttonRemoveFile = new javax.swing.JButton(); jLabel7 = new javax.swing.JLabel(); spinnerBlocksPerAttempt = new javax.swing.JSpinner(); jLabel9 = new javax.swing.JLabel(); labelBlocksPerAttempt = new javax.swing.JLabel(); jLabel10 = new javax.swing.JLabel(); buttonReloadAll.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/pepsoft/worldpainter/icons/arrow_rotate_clockwise.png"))); // NOI18N buttonReloadAll.setToolTipText("Reload all or selected objects from disk"); buttonReloadAll.setEnabled(false); buttonReloadAll.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { buttonReloadAllActionPerformed(evt); } }); jSeparator2.setOrientation(javax.swing.SwingConstants.VERTICAL); buttonEdit.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/pepsoft/worldpainter/icons/brick_edit.png"))); // NOI18N buttonEdit.setToolTipText("Edit selected object(s) options"); buttonEdit.setEnabled(false); buttonEdit.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { buttonEditActionPerformed(evt); } }); labelLeafDecayTitle.setText("Leaf decay settings for these objects:"); labelEffectiveLeafDecaySetting.setText("<html>Leaves do <b>not</b> decay.</html>"); labelEffectiveLeafDecaySetting.setEnabled(false); buttonSetDecay.setText("Set all to decay"); buttonSetDecay.setToolTipText("Set all objects to decaying leaves"); buttonSetDecay.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { buttonSetDecayActionPerformed(evt); } }); buttonSetNoDecay.setText("<html>Set all to <b>not</b> decay</html>"); buttonSetNoDecay.setToolTipText("Set all objects to non decaying leaves"); buttonSetNoDecay.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { buttonSetNoDecayActionPerformed(evt); } }); buttonReset.setText("Reset"); buttonReset.setToolTipText("Reset leaf decay to object defaults"); buttonReset.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { buttonResetActionPerformed(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(labelEffectiveLeafDecaySetting, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addGroup(jPanel3Layout.createSequentialGroup() .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(labelLeafDecayTitle) .addGroup(jPanel3Layout.createSequentialGroup() .addComponent(buttonSetDecay) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(buttonSetNoDecay, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(buttonReset))) .addGap(0, 0, Short.MAX_VALUE)) ); jPanel3Layout.setVerticalGroup( jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel3Layout.createSequentialGroup() .addComponent(labelLeafDecayTitle) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(labelEffectiveLeafDecaySetting, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(buttonSetDecay) .addComponent(buttonSetNoDecay, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(buttonReset))) ); listObjects.setModel(new javax.swing.AbstractListModel() { String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" }; public int getSize() { return strings.length; } public Object getElementAt(int i) { return strings[i]; } }); listObjects.addMouseListener(new java.awt.event.MouseAdapter() { public void mouseClicked(java.awt.event.MouseEvent evt) { listObjectsMouseClicked(evt); } }); jScrollPane1.setViewportView(listObjects); jLabel6.setForeground(new java.awt.Color(0, 0, 255)); jLabel6.setText("<html><u>Get custom objects</u></html>"); jLabel6.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR)); jLabel6.addMouseListener(new java.awt.event.MouseAdapter() { public void mouseClicked(java.awt.event.MouseEvent evt) { jLabel6MouseClicked(evt); } }); jLabel1.setText("Define your custom object layer on this screen."); jLabel3.setText("Name:"); fieldName.setColumns(10); jLabel4.setText("Colour:"); jLabel5.setBackground(java.awt.Color.orange); jLabel5.setText(" "); jLabel5.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); jLabel5.setOpaque(true); buttonPickColour.setText("..."); buttonPickColour.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { buttonPickColourActionPerformed(evt); } }); javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); jPanel2.setLayout(jPanel2Layout); jPanel2Layout.setHorizontalGroup( jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel2Layout.createSequentialGroup() .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel2Layout.createSequentialGroup() .addComponent(jLabel3) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(fieldName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(jPanel2Layout.createSequentialGroup() .addComponent(jLabel4) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jLabel5) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(buttonPickColour))) .addGap(0, 0, Short.MAX_VALUE)) ); jPanel2Layout.setVerticalGroup( jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel2Layout.createSequentialGroup() .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLabel3) .addComponent(fieldName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGap(18, 18, Short.MAX_VALUE) .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLabel4) .addComponent(jLabel5) .addComponent(buttonPickColour))) ); jLabel2.setText("Object(s):"); buttonAddFile.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/pepsoft/worldpainter/icons/brick_add.png"))); // NOI18N buttonAddFile.setToolTipText("Add one or more objects"); buttonAddFile.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { buttonAddFileActionPerformed(evt); } }); buttonRemoveFile.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/pepsoft/worldpainter/icons/brick_delete.png"))); // NOI18N buttonRemoveFile.setToolTipText("Remove selected object(s)"); buttonRemoveFile.setEnabled(false); buttonRemoveFile.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { buttonRemoveFileActionPerformed(evt); } }); jLabel7.setText("Sparseness:"); spinnerBlocksPerAttempt.setModel(new javax.swing.SpinnerNumberModel(20, 1, 100000, 1)); spinnerBlocksPerAttempt.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { spinnerBlocksPerAttemptStateChanged(evt); } }); jLabel9.setText(" blocks (at 50% intensity;"); labelBlocksPerAttempt.setText("one per x blocks at 1%; one per y blocks at 100%)"); jLabel10.setText("one object per "); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(jScrollPane1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(buttonAddFile, javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(buttonRemoveFile, javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(buttonEdit, javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(buttonReloadAll, javax.swing.GroupLayout.Alignment.TRAILING))) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jLabel2) .addComponent(jLabel1) .addComponent(jLabel6, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createSequentialGroup() .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 2, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jPanel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(layout.createSequentialGroup() .addComponent(jLabel7) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(labelBlocksPerAttempt) .addGroup(layout.createSequentialGroup() .addComponent(jLabel10) .addGap(0, 0, 0) .addComponent(spinnerBlocksPerAttempt, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, 0) .addComponent(jLabel9))))) .addGap(0, 0, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(jLabel1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jLabel6, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(18, 18, 18) .addComponent(jLabel2) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(buttonAddFile) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(buttonRemoveFile) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(buttonEdit) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(buttonReloadAll) .addGap(0, 0, Short.MAX_VALUE)) .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)) .addGap(18, 18, 18) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLabel7) .addComponent(spinnerBlocksPerAttempt, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jLabel9) .addComponent(jLabel10)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(labelBlocksPerAttempt) .addGap(18, 18, 18) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(jSeparator2) .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jPanel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) ); }// </editor-fold>//GEN-END:initComponents private void buttonReloadAllActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonReloadAllActionPerformed reloadObjects(); }//GEN-LAST:event_buttonReloadAllActionPerformed private void buttonEditActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonEditActionPerformed editObjects(); }//GEN-LAST:event_buttonEditActionPerformed private void buttonSetDecayActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonSetDecayActionPerformed setLeavesDecay(); }//GEN-LAST:event_buttonSetDecayActionPerformed private void buttonSetNoDecayActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonSetNoDecayActionPerformed setLeavesNoDecay(); }//GEN-LAST:event_buttonSetNoDecayActionPerformed private void buttonResetActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonResetActionPerformed resetLeafDecay(); }//GEN-LAST:event_buttonResetActionPerformed private void listObjectsMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_listObjectsMouseClicked if (evt.getClickCount() == 2) { int row = listObjects.getSelectedIndex(); if (row != -1) { WPObject object = (WPObject) listModel.getElementAt(row); EditObjectAttributes dialog = new EditObjectAttributes(SwingUtilities.getWindowAncestor(this), object, colourScheme); dialog.setVisible(true); if (! dialog.isCancelled()) { refreshLeafDecaySettings(); } } } }//GEN-LAST:event_listObjectsMouseClicked private void jLabel6MouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jLabel6MouseClicked try { DesktopUtils.open(new URL("http://www.worldpainter.net/trac/wiki/CustomObjects")); } catch (MalformedURLException e) { throw new RuntimeException("Malformed URL exception while trying to open http://www.worldpainter.net/trac/wiki/CustomObjects", e); } }//GEN-LAST:event_jLabel6MouseClicked private void buttonPickColourActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonPickColourActionPerformed pickColour(); }//GEN-LAST:event_buttonPickColourActionPerformed private void buttonAddFileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonAddFileActionPerformed addFilesOrDirectory(); }//GEN-LAST:event_buttonAddFileActionPerformed private void buttonRemoveFileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonRemoveFileActionPerformed removeFiles(); }//GEN-LAST:event_buttonRemoveFileActionPerformed private void spinnerBlocksPerAttemptStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spinnerBlocksPerAttemptStateChanged updateBlocksPerAttempt(); settingsChanged(); }//GEN-LAST:event_spinnerBlocksPerAttemptStateChanged // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton buttonAddFile; private javax.swing.JButton buttonEdit; private javax.swing.JButton buttonPickColour; private javax.swing.JButton buttonReloadAll; private javax.swing.JButton buttonRemoveFile; private javax.swing.JButton buttonReset; private javax.swing.JButton buttonSetDecay; private javax.swing.JButton buttonSetNoDecay; private javax.swing.JTextField fieldName; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel10; 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.JLabel jLabel7; private javax.swing.JLabel jLabel9; private javax.swing.JPanel jPanel2; private javax.swing.JPanel jPanel3; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JSeparator jSeparator2; private javax.swing.JLabel labelBlocksPerAttempt; private javax.swing.JLabel labelEffectiveLeafDecaySetting; private javax.swing.JLabel labelLeafDecayTitle; private javax.swing.JList listObjects; private javax.swing.JSpinner spinnerBlocksPerAttempt; // End of variables declaration//GEN-END:variables private final DefaultListModel listModel; private final NumberFormat numberFormat = NumberFormat.getInstance(); private ColourScheme colourScheme; private int selectedColour = Color.ORANGE.getRGB(); private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(Bo2LayerEditor.class); private static final long serialVersionUID = 1L; }