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

org.pepsoft.worldpainter.layers.EditLayerDialog 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.layers;

import org.pepsoft.worldpainter.App;
import org.pepsoft.worldpainter.ColourScheme;
import org.pepsoft.worldpainter.Dimension;
import org.pepsoft.worldpainter.Platform;
import org.pepsoft.worldpainter.biomeschemes.CustomBiomeManager;
import org.pepsoft.worldpainter.layers.exporters.ExporterSettings;
import org.pepsoft.worldpainter.layers.plants.PlantLayer;
import org.pepsoft.worldpainter.objects.MinecraftWorldObject;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Pepijn Schmitz
 * @param  The type of layer which the dialog should edit.
 */
public class EditLayerDialog extends AbstractEditLayerDialog implements LayerEditor.LayerEditorContext, ActionListener {
    /**
     * Creates new form EditLayerDialog for creating a new instance of a
     * specific layer type.
     * 
     * @param parent The window relative to which to display the dialog.
     * @param layerType The type of layer of which to create a new instance.
     */
    public EditLayerDialog(Window parent, Platform platform, Class layerType) {
        this(parent, null, LayerEditorManager.getInstance().createEditor(platform, layerType));
    }
    
    /**
     * Creates new form EditLayerDialog for editing an existing layer.
     * 
     * @param parent The window relative to which to display the dialog.
     * @param layer The layer to edit..
     */
    @SuppressWarnings("unchecked") // Guaranteed by Java
    public EditLayerDialog(Window parent, Platform platform, L layer) {
        this(parent, layer, LayerEditorManager.getInstance().createEditor(platform, (Class) layer.getClass()));
    }

    private EditLayerDialog(Window parent, L layer, LayerEditor editor) {
        super(parent);
        if (editor == null) {
            throw new IllegalArgumentException("No editor available for layer type" + ((layer != null) ? layer.getClass() : ""));
        }
        this.editor = editor;
        if (layer == null) {
            layer = editor.createLayer();
        }
        this.app = App.getInstance();
        previewCreator = LayerPreviewCreator.createPreviewerForLayer(layer, app.getDimension());

        initComponents();
        if (! (layer instanceof CustomLayer)) {
            setIconImage(layer.getIcon());
        }
        
        previewTimer.setRepeats(false);
        
        editor.setContext(this);
        editor.setLayer(layer);
        JComponent editorComponent = editor.getComponent();
        editorPanel.add(editorComponent, BorderLayout.CENTER);
        // For some strange reason the look&feel isn't applied by Swing
        SwingUtilities.updateComponentTreeUI(editorComponent);

        int index = 0;
        for (Component additionalButton: editor.getAdditionalButtons()) {
            panelButtons.add(additionalButton, index++);
        }

        buttonOK.setEnabled(editor.isCommitAvailable());
        
        jComboBox1.setModel(new DefaultComboBoxModel(LayerPreviewCreator.PATTERNS));
        jComboBox1.setRenderer(new PatternListCellRenderer());
        jComboBox1.setSelectedItem(previewCreator.getPattern());

        dynMapPreviewer1.setZoom(-2);
        dynMapPreviewer1.setInclination(30.0);
        dynMapPreviewer1.setAzimuth(60.0);

        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
                cancelPreviewUpdate();
            }
        });

        scaleToUI();
        pack();
        if (! (layer instanceof PlantLayer)) {
            scaleWindowToUI();
        }
        setLocationRelativeTo(parent);
        getRootPane().setDefaultButton(buttonOK);
    }

    // AbstractEditLayerDialog

    @Override
    public L getLayer() {
        return editor.getLayer();
    }
    
    // LayerEditorContext
    
    @Override
    public void settingsChanged() {
        boolean commitAvailable = editor.isCommitAvailable();
        buttonOK.setEnabled(commitAvailable);
        if (commitAvailable) {
            schedulePreviewUpdate();
        } else {
            cancelPreviewUpdate();
        }
    }

    @Override
    public Dimension getDimension() {
        return app.getDimension();
    }

    @Override
    public ColourScheme getColourScheme() {
        return app.getColourScheme();
    }

    @Override
    public boolean isExtendedBlockIds() {
        return app.getWorld().isExtendedBlockIds();
    }

    @Override
    public CustomBiomeManager getCustomBiomeManager() {
        return app.getCustomBiomeManager();
    }

    @Override
    public List getAllLayers() {
        return new ArrayList<>(app.getAllLayers());
    }

    // ActionListener
    
    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == previewTimer) {
            updatePreview();
        }
    }
    
    private void schedulePreviewUpdate() {
        previewTimer.restart();
    }
    
    private void cancelPreviewUpdate() {
        previewTimer.stop();
        synchronized (PREVIEW_STATE_LOCK) {
            // We can't interrupt an existing render, but we can make sure
            // another one isn't started immediately after
            if (previewRenderState == PreviewRenderState.SECOND_RENDER_SCHEDULED) {
                previewRenderState = PreviewRenderState.RENDERING;
            }
        }
    }
    
    private void updatePreview() {
        // TODO use Netherrack in addition to or instead of Grass, e.g. for Plants layer with Nether plants, etc.
        // Check again whether the current settings are valid, although the
        // chance is remote
        if (! editor.isCommitAvailable()) {
            return;
        }
        final ExporterSettings settings = editor.getSettings();
        synchronized (PREVIEW_STATE_LOCK) {
            switch (previewRenderState) {
                case IDLE:
                    // Not rendering anything yet
                    new Thread("Preview Creator for " + editor.getLayer().getName()) {
                        @Override
                        public void run() {
                            renderLoop:
                            do {
                                synchronized (PREVIEW_RENDERER_LOCK) {
                                    previewCreator.setLayer(settings.getLayer());
                                    previewCreator.setSettings(settings);
                                    final MinecraftWorldObject preview = previewCreator.renderPreview();
                                    SwingUtilities.invokeLater(() -> dynMapPreviewer1.setObject(preview, app.getDimension()));
                                }
                                synchronized (PREVIEW_STATE_LOCK) {
                                    switch (previewRenderState) {
                                        case SECOND_RENDER_SCHEDULED:
                                            previewRenderState = PreviewRenderState.RENDERING;
                                            break; // Loop again
                                        case RENDERING:
                                            previewRenderState = PreviewRenderState.IDLE;
                                            // Fall through to break loop
                                        default:
                                            // Break loop
                                            break renderLoop;
                                    }
                                }
                            } while(true);
                        }
                    }.start();
                    previewRenderState = PreviewRenderState.RENDERING;
                    break;
                case RENDERING:
                    // A preview is already being rendered; set the state such
                    // that when it is finished another render will immediately
                    // be started
                    previewRenderState = PreviewRenderState.SECOND_RENDER_SCHEDULED;
                    break;
                case SECOND_RENDER_SCHEDULED:
                    // A second render has already been scheduled for when the
                    // current render is finished. There is nothing more we can
                    // do
                    break;
            }
        }
    }

    private void updatePattern() {
        previewCreator.setPattern((LayerPreviewCreator.Pattern) jComboBox1.getSelectedItem());
        schedulePreviewUpdate();
    }

    /**
     * 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")
    // //GEN-BEGIN:initComponents
    private void initComponents() {

        editorPanel = new javax.swing.JPanel();
        dynMapPreviewer1 = new org.pepsoft.worldpainter.dynmap.DynmapPreviewer();
        jComboBox1 = new javax.swing.JComboBox();
        jLabel1 = new javax.swing.JLabel();
        panelButtons = new javax.swing.JPanel();
        buttonOK = new javax.swing.JButton();
        buttonCancel = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setTitle("Edit Layer Settings");

        editorPanel.setLayout(new java.awt.BorderLayout());

        dynMapPreviewer1.setBorder(javax.swing.BorderFactory.createEtchedBorder());

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

        jLabel1.setText("Preview pattern:");

        panelButtons.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 5, 0));

        buttonOK.setText("OK");
        buttonOK.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                buttonOKActionPerformed(evt);
            }
        });
        panelButtons.add(buttonOK);

        buttonCancel.setText("Cancel");
        buttonCancel.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                buttonCancelActionPerformed(evt);
            }
        });
        panelButtons.add(buttonCancel);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(editorPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(18, 18, 18)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(dynMapPreviewer1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addGap(0, 0, Short.MAX_VALUE)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                                .addComponent(jLabel1)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                            .addComponent(panelButtons, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(editorPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(dynMapPreviewer1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabel1))
                        .addGap(12, 12, 12)
                        .addComponent(panelButtons, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap())
        );

        pack();
    }// //GEN-END:initComponents

    private void buttonCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonCancelActionPerformed
        cancel();
    }//GEN-LAST:event_buttonCancelActionPerformed

    private void buttonOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonOKActionPerformed
        editor.commit();
        ok();
    }//GEN-LAST:event_buttonOKActionPerformed

    private void jComboBox1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jComboBox1ActionPerformed
        updatePattern();
    }//GEN-LAST:event_jComboBox1ActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton buttonCancel;
    private javax.swing.JButton buttonOK;
    private org.pepsoft.worldpainter.dynmap.DynmapPreviewer dynMapPreviewer1;
    private javax.swing.JPanel editorPanel;
    private javax.swing.JComboBox jComboBox1;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JPanel panelButtons;
    // End of variables declaration//GEN-END:variables

    private final LayerEditor editor;
    private final Timer previewTimer = new Timer(1000, this);
    private final LayerPreviewCreator previewCreator;
    private final App app;
    private PreviewRenderState previewRenderState = PreviewRenderState.IDLE;
    
    /**
     * This lock guards the preview rendering state.
     */
    private static final Object PREVIEW_STATE_LOCK = new Object();
    /**
     * The preview renderer is stateful; this lock prevents more than one thread
     * from using it at once.
     */
    private static final Object PREVIEW_RENDERER_LOCK = new Object();
    
    enum PreviewRenderState {IDLE, RENDERING, SECOND_RENDER_SCHEDULED}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy