greenfoot.gui.export.ExportPublishPane.java Source code

Java tutorial

Introduction

Here is the source code for greenfoot.gui.export.ExportPublishPane.java

Source

/*
 This file is part of the Greenfoot program. 
 Copyright (C) 2005-2009,2010,2011,2012,2013  Poul Henriksen and Michael Kolling 
     
 This program is free software; you can redistribute it and/or 
 modify it under the terms of the GNU General Public License 
 as published by the Free Software Foundation; either version 2 
 of the License, or (at your option) any later version. 
     
 This program is distributed in the hope that it will be useful, 
 but WITHOUT ANY WARRANTY; without even the implied warranty of 
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 GNU General Public License for more details. 
     
 You should have received a copy of the GNU General Public License 
 along with this program; if not, write to the Free Software 
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. 
     
 This file is subject to the Classpath exception as provided in the  
 LICENSE.txt file that accompanied this code.
 */
package greenfoot.gui.export;

import greenfoot.core.GProject;
import greenfoot.export.mygame.ExistingScenarioChecker;
import greenfoot.export.mygame.MyGameClient;
import greenfoot.export.mygame.ScenarioInfo;
import greenfoot.util.GreenfootUtil;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.InputVerifier;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.apache.commons.httpclient.ConnectTimeoutException;

import bluej.Config;
import bluej.utility.Debug;
import bluej.utility.MiksGridLayout;
import bluej.utility.SwingWorker;

/**
 * Pane used for exporting to Greenfoot Gallery
 * 
 * @author Michael Kolling
 * @author Poul Henriksen
 */
public class ExportPublishPane extends ExportPane implements ChangeListener {
    public static final int IMAGE_WIDTH = 120;
    public static final int IMAGE_HEIGHT = 70;

    public static final String FUNCTION = "PUBLISH";
    private static final Color background = new Color(166, 188, 202);
    private static final Color headingColor = new Color(40, 75, 125);
    private static final String serverURL = ensureTrailingSlash(
            Config.getPropString("greenfoot.gameserver.address", "http://www.greenfoot.org/"));
    private static final String createAccountUrl = Config
            .getPropString("greenfoot.gameserver.createAccount.address", "http://www.greenfoot.org/users/new");
    private static final String serverName = Config.getPropString("greenfoot.gameserver.name", "Greenfoot Gallery");

    private static final String helpLine1 = Config.getString("export.publish.help") + " " + serverName;
    private static final String WITH_SOURCE_TAG = "with-source";

    private JComponent leftPanel;
    private JPanel titleAndDescPanel;
    private JPanel infoPanel;
    private JTextField titleField;
    private JTextField shortDescriptionField;
    private JTextArea descriptionArea;
    private JTextArea updateArea;
    private JTextField urlField;
    private JTextField userNameField;
    private JPasswordField passwordField;
    private ImageEditPanel imagePanel;
    private JCheckBox includeSource;
    private JCheckBox keepScenarioScreenshot;

    private SwingWorker commonTagsLoader;
    private JCheckBox[] popTags = new JCheckBox[7];
    private JTextArea tagArea;
    private GProject project;
    private boolean firstActivation = true;

    private ScenarioInfo publishedScenarioInfo;
    private String publishedUserName;

    private ExistingScenarioChecker scenarioChecker;
    private Font font;
    private boolean isUpdate = false;
    private ExportDialog exportDialog;

    /** Creates a new instance of ExportPublishPane */
    public ExportPublishPane(GProject project, ExportDialog exportDialog) {
        super();
        this.project = project;
        this.exportDialog = exportDialog;
        makePane();
    }

    /**
     * Get the image that is to be used as icon for this scenario.
     * 
     * @return The image, or null if it couldn't be created.
     */
    public BufferedImage getImage() {
        return imagePanel.getImage();
    }

    /**
     * Set the screenshot image.
     */
    public void setImage(BufferedImage snapShot) {
        imagePanel.setImage(snapShot);
        imagePanel.repaint();
    }

    /**
     * Get the scenario title, specified in the title field.
     */
    public String getTitle() {
        if (titleField != null) {
            return titleField.getText();
        }
        return null;
    }

    /**
     * Return the short description string.
     */
    public String getShortDescription() {
        return shortDescriptionField.getText();
    }

    /**
     * Return the description string.
     */
    public String getDescription() {
        return descriptionArea.getText();
    }

    /**
     * Return the URL.
     */
    public String getURL() {
        return urlField.getText();
    }

    /**
     * Return the user name.
     */
    public String getUserName() {
        return userNameField.getText();
    }

    /**
     * Return the changes to update string (if applicable)
     */
    public String getUpdateDescription() {
        if (updateArea != null) {
            return updateArea.getText();
        }
        return null;
    }

    /**
     * Return the password.
     */
    public String getPassword() {
        return new String(passwordField.getPassword());
    }

    /**
     * True if the source code should be included.
     */
    public boolean includeSourceCode() {
        return includeSource.isSelected();
    }

    private void setHasSource(boolean hasSource) {
        includeSource.setSelected(hasSource);
    }

    /**
     * True if the screenshot should *not* be overwritten; false if it should
     */
    public boolean keepSavedScenarioScreenshot() {
        if (isUpdate && keepScenarioScreenshot != null) {
            return keepScenarioScreenshot.isSelected();
        }
        return false;
    }

    private void setLocked(boolean locked) {
        lockScenario.setSelected(locked);
    }

    private void setTags(List<String> tags) {
        StringBuilder newTags = new StringBuilder();
        boolean isFirstNewTag = true;
        for (Iterator<String> iterator = tags.iterator(); iterator.hasNext();) {
            String tag = iterator.next();
            if (WITH_SOURCE_TAG.equals(tag)) {
                // we never want the with-source tag to show up.
                continue;
            }
            boolean isPopTag = false;
            for (int i = 0; i < popTags.length; i++) {
                JCheckBox popTag = popTags[i];
                if (popTag.getText().equals(tag)) {
                    popTag.setSelected(true);
                    isPopTag = true;
                    break;
                }
            }
            if (!isPopTag) {
                if (!isFirstNewTag) {
                    // Only insert newline if it is not the first new tag
                    newTags.append(System.getProperty("line.separator"));
                }
                isFirstNewTag = false;
                newTags.append(tag);
            }
        }
        tagArea.setText(newTags.toString());
    }

    private void setUrl(String url) {
        urlField.setText(url);
    }

    private void setLongDescription(String longDescription) {
        descriptionArea.setText(longDescription);
    }

    private void setShortDescripton(String shortDescription) {
        shortDescriptionField.setText(shortDescription);
    }

    private void setTitle(String title) {
        titleField.setText(title);
    }

    private void setUserName(String name) {
        userNameField.setText(name);
    }

    /**
     * Build the component.
     */
    private void makePane() {
        font = (new JLabel()).getFont().deriveFont(Font.ITALIC, 11.0f);

        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        setBorder(BorderFactory.createEmptyBorder(12, 12, 0, 12));
        setBackground(backgroundColor);

        add(getHelpBox());
        add(Box.createVerticalStrut(12));

        infoPanel = new JPanel(new BorderLayout(22, 18));
        {
            infoPanel.setAlignmentX(LEFT_ALIGNMENT);
            infoPanel.setBackground(background);

            Border border = BorderFactory.createCompoundBorder(BorderFactory.createLoweredBevelBorder(),
                    BorderFactory.createEmptyBorder(12, 22, 12, 22));
            infoPanel.setBorder(border);

            JLabel text = new JLabel(Config.getString("export.publish.info") + " " + serverName,
                    SwingConstants.CENTER);
            text.setForeground(headingColor);
            infoPanel.add(text, BorderLayout.NORTH);

            createScenarioDisplay();
            infoPanel.add(leftPanel, BorderLayout.CENTER);
            infoPanel.add(getTagDisplay(), BorderLayout.EAST);
        }

        add(infoPanel);
        add(Box.createVerticalStrut(16));
        add(getLoginPanel());
        add(Box.createVerticalStrut(10));
    }

    /**
     * Creates a login panel with a username and password and a create account option
     * @return Login panel Component
     */
    private JComponent getLoginPanel() {
        JComponent loginPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 8, 4));

        loginPanel.setBackground(background);
        loginPanel.setAlignmentX(LEFT_ALIGNMENT);
        Border border = BorderFactory.createCompoundBorder(BorderFactory.createLoweredBevelBorder(),
                BorderFactory.createEmptyBorder(12, 12, 12, 12));
        loginPanel.setBorder(border);

        JLabel text = new JLabel(Config.getString("export.publish.login"));
        text.setForeground(headingColor);
        text.setVerticalAlignment(SwingConstants.TOP);
        loginPanel.add(text);

        text = new JLabel(Config.getString("export.publish.username"), SwingConstants.TRAILING);
        text.setFont(font);
        loginPanel.add(text);
        userNameField = new JTextField(10);
        userNameField.setInputVerifier(new InputVerifier() {
            @Override
            public boolean verify(JComponent input) {
                String text = userNameField.getText();
                return text.length() > 0;
            }
        });
        userNameField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                checkForExistingScenario();
            }
        });
        loginPanel.add(userNameField);
        text = new JLabel(Config.getString("export.publish.password"), SwingConstants.TRAILING);
        text.setFont(font);
        loginPanel.add(text);
        passwordField = new JPasswordField(10);
        loginPanel.add(passwordField);

        JLabel createAccountLabel = new JLabel(Config.getString("export.publish.createAccount"));
        {
            createAccountLabel.setBackground(background);

            createAccountLabel.setHorizontalAlignment(SwingConstants.RIGHT);
            GreenfootUtil.makeLink(createAccountLabel, createAccountUrl);
        }
        loginPanel.add(createAccountLabel);
        return loginPanel;
    }

    /**
     * Build a help box with a link to appropriate help
     * @return help box
     */
    private static Box getHelpBox() {
        Box helpBox = new Box(BoxLayout.X_AXIS);
        helpBox.setAlignmentX(LEFT_ALIGNMENT);
        JLabel helpText1 = new JLabel(helpLine1 + " (");
        helpBox.add(helpText1);
        JLabel serverLink = new JLabel(serverURL);
        GreenfootUtil.makeLink(serverLink, serverURL);
        helpBox.add(serverLink);
        helpBox.add(new JLabel(")"));
        return helpBox;
    }

    /**
     * Set the tags in the UI from the given list (null if the server couldn't be contacted or
     * didn't respond as expected).
     * 
     * <p>Should be called from event thread
     */
    private void setPopularTags(List<String> tags) {
        if (tags == null) {
            // Couldn't get the tags list.
            popTags[0].setText("Unavailable");
            for (int i = 1; i < popTags.length; i++) {
                popTags[i].setText("");
            }
            return;
        }

        int minLength = popTags.length < tags.size() ? popTags.length : tags.size();
        for (int i = 0; i < minLength; i++) {
            JCheckBox checkBox = popTags[i];
            checkBox.setText(tags.get(i));
            checkBox.setEnabled(true);
            setTags(getTags());
        }

        // Clear any remaining checkboxes.
        for (int i = minLength; i < popTags.length; i++) {
            popTags[i].setText("");
        }
    }

    /**
     * Returns a list of the tags that the user chose for this scenario.
     */
    public List<String> getTags() {
        List<String> tagList = new LinkedList<String>();

        for (int i = 0; i < popTags.length; i++) {
            JCheckBox checkBox = popTags[i];
            if (checkBox.isSelected()) {
                tagList.add(checkBox.getText());
            }
        }

        String currentTags = tagArea.getText().trim();
        String[] tags = currentTags.split("\\s");
        for (int i = 0; i < tags.length; i++) {
            String tag = tags[i].trim();
            if (!tag.equals("")) {
                tagList.add(tag);
            }
        }

        if (includeSourceCode() && !tagList.contains(WITH_SOURCE_TAG)) {
            tagList.add(WITH_SOURCE_TAG);
        } else if (!includeSourceCode()) {
            tagList.remove(WITH_SOURCE_TAG);
        }
        return tagList;
    }

    /**
     * Attempts to load details already stored for this scenario at previous
     * publish.
     * 
     * Must be called from the event thread.
     */
    private void loadStoredScenarioInfo() {
        ScenarioInfo info = new ScenarioInfo();
        if (info.load(project.getProjectProperties())) {
            setTitle(info.getTitle());
            setShortDescripton(info.getShortDescription());
            setLongDescription(info.getLongDescription());
            setUrl(info.getUrl());
            setTags(info.getTags());
            setLocked(info.isLocked());
            setHasSource(info.getHasSource());
            setUpdate(true);
        }
    }

    /**
     * Update the display according to whether this is an update of an existing scenario,
     * or an upload of a new scenario.
     */
    private void updateScenarioDisplay() {
        removeLeftPanel();
        createScenarioDisplay();
        infoPanel.add(leftPanel, BorderLayout.CENTER);
        boolean enableImageControl = !isUpdate || !keepScenarioScreenshot.isSelected();
        imagePanel.enableImageEditPanel(enableImageControl);
        revalidate();
    }

    /**
     * Updates the given scenarioInfo with the current values typed into the
     * dialog.
     */
    private void updateInfoFromFields(ScenarioInfo scenarioInfo) {
        scenarioInfo.setTitle(getTitle());
        scenarioInfo.setShortDescription(getShortDescription());
        scenarioInfo.setLongDescription(getDescription());
        scenarioInfo.setUrl(getURL());
        scenarioInfo.setTags(getTags());
        scenarioInfo.setLocked(lockScenario());
        scenarioInfo.setHasSource(includeSourceCode());
        scenarioInfo.setUpdateDescription(getUpdateDescription());
    }

    private void checkForExistingScenario() {
        String userName = getUserName();
        String title = getTitle();

        // First check if everything is ready and bail out if it is not.
        if (userName == null || userName.equals("")) {
            return;
        }
        if (title == null || title.equals("")) {
            return;
        }

        if (scenarioChecker == null) {
            scenarioChecker = new ExistingScenarioChecker() {

                @Override
                public void scenarioExistenceCheckFailed(Exception reason) {
                    // Don't do anything. Failure could be due to proxy requiring authentication,
                    // or network disconnection.
                }

                @Override
                public void scenarioExistenceChecked(ScenarioInfo info) {
                    if (info != null) {
                        setUpdate(true);
                    } else {
                        setUpdate(false);
                    }
                }
            };
        }
        scenarioChecker.startScenarioExistenceCheck(serverURL, userName, title);
    }

    /**
     * The first time this pane is activated we fetch the popular tags from the
     * server (if possible).
     * 
     * <p>And we load previously used values if they are stored.
     */
    @Override
    public void activated() {
        if (firstActivation) {
            firstActivation = false;

            setUserName(Config.getPropString("publish.username", ""));
            loadStoredScenarioInfo();
            checkForExistingScenario();

            commonTagsLoader = new SwingWorker() {
                @SuppressWarnings("unchecked")
                @Override
                public void finished() {
                    List<String> l = (List<String>) getValue();
                    setPopularTags(l);
                }

                @Override
                public Object construct() {
                    MyGameClient client = new MyGameClient(null);
                    List<String> tags = null;
                    try {
                        String hostAddress = serverURL;
                        if (!hostAddress.endsWith("/")) {
                            hostAddress += "/";
                        }
                        // We add one to the number, because WITH_SOURCE is
                        // likely to be among them and we then will filter it
                        // out.
                        tags = client.getCommonTags(hostAddress, popTags.length + 1);
                        if (tags.contains(WITH_SOURCE_TAG)) {
                            tags.remove(WITH_SOURCE_TAG);
                        } else {
                            if (!tags.isEmpty()) {
                                tags.remove(tags.size() - 1);
                            }
                        }
                    } catch (ConnectTimeoutException ctoe) {
                    } catch (UnknownHostException e) {
                        Debug.reportError("Error while publishing scenario", e);
                    } catch (IOException e) {
                        Debug.reportError("Error while publishing scenario", e);
                    }
                    return tags;
                }
            };
            commonTagsLoader.start();
        }

        String updateText;
        if (isUpdate) {
            updateText = Config.getString("export.dialog.update");
        } else {
            updateText = Config.getString("export.dialog.share");
        }

        exportDialog.setExportButtonText(updateText);
    }

    @Override
    public boolean prePublish() {
        publishedScenarioInfo = new ScenarioInfo();
        updateInfoFromFields(publishedScenarioInfo);
        publishedUserName = userNameField.getText();
        return true;
    }

    @Override
    public void postPublish(boolean success) {
        if (success) {
            publishedScenarioInfo.store(project.getProjectProperties());
            Config.putPropString("publish.username", publishedUserName);
            setUpdate(true);
        }
    }

    /**
     * Make sure a host name ends with a slash.
     */
    public static String ensureTrailingSlash(String hostname) {
        if (hostname.endsWith("/")) {
            return hostname;
        }
        return hostname + "/";
    }

    /**
     * Creates the scenario information display including information such as title, description, url.
     * For an update (isUpdate = true), the displayed options are slightly different.
     */
    private void createScenarioDisplay() {
        leftPanel = new Box(BoxLayout.Y_AXIS);
        JLabel text;
        MiksGridLayout titleAndDescLayout = new MiksGridLayout(6, 2, 8, 8);
        titleAndDescLayout.setVerticallyExpandingRow(3);

        titleAndDescPanel = new JPanel(titleAndDescLayout);
        titleAndDescPanel.setBackground(background);

        if (imagePanel == null) {
            imagePanel = new ImageEditPanel(IMAGE_WIDTH, IMAGE_HEIGHT);
            imagePanel.setBackground(background);
        }

        Box textPanel = new Box(BoxLayout.Y_AXIS);
        {
            text = new JLabel(Config.getString("export.publish.image1"));
            text.setAlignmentX(Component.RIGHT_ALIGNMENT);
            text.setFont(font);
            textPanel.add(text);
            text = new JLabel(Config.getString("export.publish.image2"));
            text.setAlignmentX(Component.RIGHT_ALIGNMENT);
            text.setFont(font);
            textPanel.add(text);
        }
        titleAndDescPanel.add(textPanel);
        titleAndDescPanel.add(imagePanel);

        if (isUpdate) {
            text = new JLabel(Config.getString("export.snapshot.label"), SwingConstants.TRAILING);
            text.setFont(font);
            titleAndDescPanel.add(text);

            keepScenarioScreenshot = new JCheckBox();
            keepScenarioScreenshot.setSelected(true);
            // "keep screenshot" defaults to true, therefore the image panel should be disabled
            imagePanel.enableImageEditPanel(false);
            keepScenarioScreenshot.setName(Config.getString("export.publish.keepScenario"));
            keepScenarioScreenshot.setOpaque(false);
            keepScenarioScreenshot.addChangeListener(this);
            titleAndDescPanel.add(keepScenarioScreenshot);
        }

        text = new JLabel(Config.getString("export.publish.title"), SwingConstants.TRAILING);
        text.setFont(font);
        titleAndDescPanel.add(text);

        String title = project.getName();
        if (getTitle() != null) {
            title = getTitle();
        }
        titleField = new JTextField(title);
        titleField.setInputVerifier(new InputVerifier() {
            @Override
            public boolean verify(JComponent input) {
                String text = titleField.getText();
                return text.length() > 0;
            }
        });
        titleField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                checkForExistingScenario();
            }
        });
        titleAndDescPanel.add(titleField);

        // If there is an update a "changes" description area is shown.
        // If not there a short description and long description area are shown.
        if (isUpdate) {
            JLabel updateLabel = new JLabel(Config.getString("export.publish.update"), SwingConstants.TRAILING);
            updateLabel.setVerticalAlignment(SwingConstants.TOP);
            updateLabel.setFont(font);

            updateArea = new JTextArea();
            updateArea.setRows(6);
            updateArea.setLineWrap(true);
            updateArea.setWrapStyleWord(true);
            JScrollPane updatePane = new JScrollPane(updateArea);

            titleAndDescPanel.add(updateLabel);
            titleAndDescPanel.add(updatePane);
            titleAndDescLayout.setVerticallyExpandingRow(4);
        } else {
            text = new JLabel(Config.getString("export.publish.shortDescription"), SwingConstants.TRAILING);
            text.setFont(font);
            shortDescriptionField = new JTextField();
            titleAndDescPanel.add(text);
            titleAndDescPanel.add(shortDescriptionField);
            text = new JLabel(Config.getString("export.publish.longDescription"), SwingConstants.TRAILING);
            text.setVerticalAlignment(SwingConstants.TOP);
            text.setFont(font);

            descriptionArea = new JTextArea();
            descriptionArea.setRows(6);
            descriptionArea.setLineWrap(true);
            descriptionArea.setWrapStyleWord(true);
            JScrollPane description = new JScrollPane(descriptionArea);
            titleAndDescPanel.add(text);
            titleAndDescPanel.add(description);
        }

        text = new JLabel(Config.getString("export.publish.url"), SwingConstants.TRAILING);
        text.setFont(font);
        titleAndDescPanel.add(text);

        urlField = new JTextField();
        titleAndDescPanel.add(urlField);

        leftPanel.add(titleAndDescPanel, BorderLayout.SOUTH);

        JComponent sourceAndLockPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 8, 0));
        {
            sourceAndLockPanel.setBackground(background);
            includeSource = new JCheckBox(Config.getString("export.publish.includeSource"));
            includeSource.setOpaque(false);
            includeSource.setSelected(false);
            includeSource.setFont(font);
            sourceAndLockPanel.add(includeSource);
            lockScenario.setFont(font);
            sourceAndLockPanel.add(lockScenario);
            sourceAndLockPanel.setMaximumSize(sourceAndLockPanel.getPreferredSize());
        }

        leftPanel.add(sourceAndLockPanel, BorderLayout.SOUTH);
    }

    /**
     * Removes the scenario information display
     */
    private void removeLeftPanel() {
        leftPanel.removeAll();
        infoPanel.remove(leftPanel);
    }

    /**
     * Creates the tag display with popular tags and an option to add tags
     */
    private JComponent getTagDisplay() {
        JComponent tagPanel = new JPanel(new MiksGridLayout(3, 1, 8, 8));
        {
            tagPanel.setBackground(background);
            JComponent popPanel = new JPanel(new MiksGridLayout(8, 1, 8, 0));
            popPanel.setBackground(background);
            JLabel popLabel = new JLabel(Config.getString("export.publish.tags.popular"), SwingConstants.LEADING);
            popLabel.setFont(font);
            popPanel.add(popLabel);
            for (int i = 0; i < popTags.length; i++) {
                JCheckBox popTag = new JCheckBox(Config.getString("export.publish.tags.loading"));
                popTag.setBackground(background);
                popTag.setFont(font);
                popTag.setEnabled(false);
                popTags[i] = popTag;
                popPanel.add(popTag);
            }

            tagPanel.add(popPanel);

            Box textPanel = new Box(BoxLayout.Y_AXIS);
            {
                JLabel additionalLabel = new JLabel(Config.getString("export.publish.tags.additional1"),
                        SwingConstants.LEADING);
                additionalLabel.setFont(font);
                textPanel.add(additionalLabel);

                JLabel additionalLabel2 = new JLabel(Config.getString("export.publish.tags.additional2"),
                        SwingConstants.CENTER);
                additionalLabel2.setFont(font);
                textPanel.add(additionalLabel2);
            }
            tagPanel.add(textPanel);

            tagArea = new JTextArea();
            tagArea.setRows(3);
            JScrollPane tagScroller = new JScrollPane(tagArea);
            tagPanel.add(tagScroller);
        }
        return tagPanel;
    }

    /**
     * Of interest is the state of the keep scenario checkbox as that
     * determines whether the image panel is enabled or disabled
     */
    @Override
    public void stateChanged(ChangeEvent e) {
        if (e.getSource().equals(keepScenarioScreenshot)) {
            if (keepScenarioScreenshot.isSelected()) {
                imagePanel.enableImageEditPanel(false);
            } else {
                imagePanel.enableImageEditPanel(true);
            }
        }

    }

    /**
     * Returns true if it is an update and false if it is a first export
     */
    public boolean isUpdate() {
        return isUpdate;
    }

    /**
     * Specify whether this scenario will be updated, or is a new export.
     */
    private void setUpdate(boolean isUpdate) {
        if (this.isUpdate != isUpdate) {
            this.isUpdate = isUpdate;
            if (isUpdate) {
                String updateText = Config.getString("export.dialog.update");
                exportDialog.setExportButtonText(updateText);
            } else {
                String exportText = Config.getString("export.dialog.share");
                exportDialog.setExportButtonText(exportText);
            }
            updateScenarioDisplay();
        }
    }
}