com.anrisoftware.prefdialog.simpledialog.SimpleDialog.java Source code

Java tutorial

Introduction

Here is the source code for com.anrisoftware.prefdialog.simpledialog.SimpleDialog.java

Source

/*
 * Copyright 2012-2016 Erwin Mller <erwin.mueller@deventm.org>
 *
 * This file is part of prefdialog-dialog.
 *
 * prefdialog-dialog is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * prefdialog-dialog 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 Lesser General Public License
 * along with prefdialog-dialog. If not, see <http://www.gnu.org/licenses/>.
 */
package com.anrisoftware.prefdialog.simpledialog;

import static com.anrisoftware.prefdialog.simpledialog.SimpleDialogModule.getSimpleDialogFactory;
import static java.awt.event.KeyEvent.VK_ESCAPE;
import static javax.swing.JComponent.WHEN_IN_FOCUSED_WINDOW;
import static javax.swing.KeyStroke.getKeyStroke;
import static javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE;
import static org.apache.commons.lang3.Validate.notNull;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;
import java.util.Locale;

import javax.inject.Inject;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JRootPane;

import com.anrisoftware.prefdialog.miscswing.awtcheck.OnAwt;
import com.anrisoftware.resources.images.api.IconSize;
import com.anrisoftware.resources.images.api.ImageResource;
import com.anrisoftware.resources.images.api.Images;
import com.anrisoftware.resources.images.api.ImagesFactory;
import com.anrisoftware.resources.texts.api.Texts;
import com.anrisoftware.resources.texts.api.TextsFactory;

/**
 * Simple dialog with approve, restore and cancel buttons.
 *
 * @author Erwin Mueller, erwin.mueller@deventm.org
 * @since 3.0
 */
public class SimpleDialog {

    /**
     * Status of the dialog.
     *
     * @author Erwin Mueller, erwin.mueller@deventm.org
     * @since 3.0
     */
    public enum Status {

        /**
         * The dialog was opened. Initial state of the dialog.
         *
         * @since 3.1
         */
        OPENED,

        /**
         * The user approved the dialog.
         */
        APPROVED,

        /**
         * The user canceled the dialog.
         */
        CANCELED;
    }

    /**
     * Decorates the dialog.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param dialog
     *            the {@link JDialog}.
     *
     * @param fieldsPanel
     *            the fields panel {@link Component} for the dialog.
     *
     * @param texts
     *            the {@link Texts} resources.
     *
     * @see SimpleDialogFactory#create()
     *
     * @return the {@link SimpleDialog}.
     */
    public static SimpleDialog decorate(JDialog dialog, Component fieldsPanel) {
        SimpleDialog simpleDialog = getSimpleDialogFactory().create();
        simpleDialog.setFieldsPanel(fieldsPanel);
        simpleDialog.setDialog(dialog);
        return simpleDialog.createDialog();
    }

    /**
     * Status property.
     *
     * @see #setStatus(Status)
     */
    public static final String STATUS_PROPERTY = "status";

    /**
     * Approve button name.
     */
    public static final String APPROVE_BUTTON_NAME = "approveButton";

    /**
     * Restore button name.
     */
    public static final String RESTORE_BUTTON_NAME = "restoreButton";

    /**
     * Cancel button name.
     */
    public static final String CANCEL_BUTTON_NAME = "cancelButton";

    /**
     * Error text component name.
     *
     * @since 3.1
     */
    public static final String ERROR_TEXT_NAME = "errorText";

    static final String CANCEL_ACTION_NAME = "cancel_action";

    static final String APPROVE_ACTION_NAME = "approve_action";

    static final String RESTORE_ACTION_NAME = "restore_action";

    private final VetoableChangeSupport vetoableSupport;

    private CancelAction cancelAction;

    private ApproveAction approveAction;

    @Inject
    private UiDialogPanel dialogPanel;

    private RestoreAction restoreAction;

    private Component fieldsPanel;

    private JDialog dialog;

    private Status status;

    private Texts texts;

    private Images images;

    private boolean showRestoreButton;

    private ImageResource emptyIcon;

    private ImageResource errorIcon;

    private boolean showApproveButton;

    /**
     * @see SimpleDialogFactory#create()
     */
    @Inject
    protected SimpleDialog() {
        this.vetoableSupport = new VetoableChangeSupport(this);
        this.showApproveButton = true;
        this.showRestoreButton = true;
    }

    @Inject
    void setTextsFactory(TextsFactory factory) {
        this.texts = factory.create(SimpleDialog.class.getSimpleName());
    }

    @Inject
    void setImagesFactory(ImagesFactory factory) {
        this.images = factory.create(SimpleDialog.class.getSimpleName());
    }

    @Inject
    void setApproveAction(ApproveAction action) {
        this.approveAction = action;
    }

    @Inject
    void setCancelAction(CancelAction action) {
        this.cancelAction = action;
    }

    @Inject
    void setRestoreAction(RestoreAction action) {
        this.restoreAction = action;
    }

    /**
     * Sets the panel component with the fields of the dialog.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param panel
     *            the panel {@link Component}.
     */
    @OnAwt
    public void setFieldsPanel(Component panel) {
        this.fieldsPanel = panel;
    }

    /**
     * Sets the texts resources for the dialog. There should be the resources
     * available:
     * <ul>
     * <li>{@value #APPROVE_ACTION_NAME}
     * <li>{@value #CANCEL_ACTION_NAME}
     * <li>{@value #RESTORE_ACTION_NAME}
     * </ul>
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param texts
     *            the {@link Texts} resources.
     */
    @OnAwt
    public void setTexts(Texts texts) {
        this.texts = texts;
    }

    /**
     * Returns the texts resources for the dialog.
     *
     * @return the {@link Texts} resources.
     */
    public Texts getTexts() {
        return texts;
    }

    /**
     * Sets the images resources for the dialog.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param images
     *            the {@link Images} resources.
     */
    @OnAwt
    public void setImages(Images images) {
        this.images = images;
        loadImages();
    }

    /**
     * Returns the images resources for the dialog.
     *
     * @return the {@link Images} resources.
     */
    public Images getImages() {
        return images;
    }

    /**
     * Sets the locale of the components of the dialog.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param locale
     *            the {@link Locale}.
     */
    @OnAwt
    public void setLocale(Locale locale) {
        approveAction.setLocale(locale);
        restoreAction.setLocale(locale);
        cancelAction.setLocale(locale);
        dialogPanel.setLocale(locale);
        if (dialog != null) {
            dialog.setLocale(locale);
        }
        if (fieldsPanel != null) {
            fieldsPanel.setLocale(locale);
        }
    }

    /**
     * Returns the locale of the components of the dialog.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @return the {@link Locale}.
     */
    @OnAwt
    public Locale getLocale() {
        return dialogPanel.getLocale();
    }

    /**
     * Sets to show the restore button.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param show
     *            set to {@code true} to show the restore button.
     *
     * @since 3.1
     */
    @OnAwt
    public void setShowRestoreButton(boolean show) {
        boolean oldValue = this.showRestoreButton;
        this.showRestoreButton = show;
        if (oldValue && !show) {
            if (dialogPanel != null) {
                removeRestoreButton();
            }
        }
        if (!oldValue && show) {
            readdRestoreButton();
        }
    }

    /**
     * Sets to show the approve button.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param show
     *            set to {@code true} to show the approve button.
     *
     * @since 3.2
     */
    @OnAwt
    public void setShowApproveButton(boolean show) {
        boolean oldValue = this.showApproveButton;
        this.showApproveButton = show;
        if (oldValue && !show) {
            if (dialogPanel != null) {
                removeApproveButton();
            }
        }
        if (!oldValue && show) {
            readdApproveButton();
        }
    }

    /**
     * Sets the approval action name. The action name is used to look up the
     * action resources.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param name
     *            the action name {@link String}.
     */
    @OnAwt
    public void setApproveActionName(String name) {
        approveAction.setActionName(name);
    }

    /**
     * Sets the cancel action name. The action name is used to look up the
     * action resources.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param name
     *            the action name {@link String}.
     */
    @OnAwt
    public void setCancelActionName(String name) {
        cancelAction.setActionName(name);
    }

    /**
     * Sets the restore action name. The action name is used to look up the
     * action resources.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param name
     *            the action name {@link String}.
     */
    @OnAwt
    public void setRestoreActionName(String name) {
        restoreAction.setActionName(name);
    }

    /**
     * Adds a new action listener to the approval dialog button.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param action
     *            the {@link ActionListener}.
     */
    @OnAwt
    public void addApprovalAction(ActionListener action) {
        getApproveButton().addActionListener(action);
    }

    /**
     * Adds a new action listener to the cancel dialog button.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param action
     *            the {@link ActionListener}.
     */
    @OnAwt
    public void addCancelAction(ActionListener action) {
        getCancelButton().addActionListener(action);
    }

    /**
     * Adds a new action listener to the restore dialog button.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param action
     *            the {@link ActionListener}.
     */
    @OnAwt
    public void addRestoreAction(ActionListener action) {
        getRestoreButton().addActionListener(action);
    }

    /**
     * Sets the dialog.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param dialog
     *            the {@link JDialog}.
     */
    @OnAwt
    public void setDialog(JDialog dialog) {
        this.dialog = dialog;
    }

    /**
     * Returns the dialog.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @return the {@link JDialog}.
     */
    @OnAwt
    public JDialog getDialog() {
        return dialog;
    }

    /**
     * Sets the size for the dialog.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param size
     *            sets the {@link Dimension} size.
     *
     * @since 3.5
     */
    @OnAwt
    public void setSize(Dimension size) {
        dialog.setSize(size);
    }

    /**
     * Returns the size for the dialog.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @return sets the {@link Dimension} size.
     *
     * @since 3.5
     */
    @OnAwt
    public Dimension getSize() {
        return dialog.getSize();
    }

    /**
     * Opens or closes the dialog.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param visible
     *            set to {@code true} to open the dialog.
     *
     * @throws PropertyVetoException
     *             if the opening of the dialog was vetoed.
     *
     * @since 3.5
     */
    @OnAwt
    public void setVisible(boolean visible) throws PropertyVetoException {
        if (visible) {
            openDialog();
        } else {
            closeDialog();
        }
    }

    /**
     * Creates the simple dialog and all child fields.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @return this {@link SimpleDialog}.
     */
    @OnAwt
    public SimpleDialog createDialog() {
        loadImages();
        setupPanel();
        setupActions();
        setupDialog();
        setupKeyboardActions();
        return this;
    }

    /**
     * Opens the dialog.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @throws PropertyVetoException
     *             if the opening of the dialog was vetoed.
     *
     * @since 3.5
     */
    @OnAwt
    public void openDialog() throws PropertyVetoException {
        setStatus(Status.OPENED);
        dialog.setVisible(true);
    }

    /**
     * Closes the dialog.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     */
    @OnAwt
    public void closeDialog() {
        dialog.setVisible(false);
    }

    /**
     * Packs the dialog.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     */
    @OnAwt
    public void packDialog() {
        dialog.pack();
    }

    /**
     * Sets the location of the dialog relative to the owner.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param owner
     *            the {@link Component} owner.
     */
    @OnAwt
    public void setLocationRelativeTo(Component owner) {
        dialog.setLocationRelativeTo(owner);
    }

    /**
     * Restores the input of the dialog.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     */
    @OnAwt
    public void restoreDialog() {
    }

    /**
     * Returns the component to be added in a container. The component contains
     * the dialog button and fields.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @return the {@link Component}.
     */
    @OnAwt
    public Component getAWTComponent() {
        return dialogPanel;
    }

    /**
     * Returns the approval dialog button.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @return the approval {@link JButton}.
     *
     * @since 3.2
     */
    @OnAwt
    public JButton getApproveButton() {
        return dialogPanel.getApproveButton();
    }

    /**
     * Returns the reset dialog button.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @return the reset {@link JButton}.
     */
    @OnAwt
    public JButton getRestoreButton() {
        return dialogPanel.getRestoreButton();
    }

    /**
     * Returns the cancel dialog button.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @return the cancel {@link JButton}.
     */
    @OnAwt
    public JButton getCancelButton() {
        return dialogPanel.getCancelButton();
    }

    /**
     * Sets the status of the dialog. The property change listeners are informed
     * of the status change.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param status
     *            the {@link Status}.
     *
     * @throws PropertyVetoException
     *             if the status was vetoed by one of the property change
     *             listener.
     *
     * @see #STATUS_PROPERTY
     */
    @OnAwt
    public void setStatus(Status status) throws PropertyVetoException {
        Status oldValue = this.status;
        vetoableSupport.fireVetoableChange(STATUS_PROPERTY, oldValue, status);
        this.status = status;
    }

    /**
     * Returns the status of the dialog.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @return the {@link Status} or {@code null} if the dialog was not open
     *         yet.
     */
    @OnAwt
    public Status getStatus() {
        return status;
    }

    /**
     * Sets the error text to show inside the dialog.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param text
     *            the error {@link String} text or {@code null}.
     *
     * @since 3.1
     */
    @OnAwt
    public void setErrorText(String text) {
        if (text != null) {
            dialogPanel.getErrorTextLabel().setIcon(new ImageIcon(errorIcon.getImage()));
            dialogPanel.getErrorTextLabel().setText(text);
        } else {
            dialogPanel.getErrorTextLabel().setIcon(new ImageIcon(emptyIcon.getImage()));
            dialogPanel.getErrorTextLabel().setText(null);
        }
    }

    /**
     * Sets the error text font.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param font
     *            the {@link Font}.
     * @since 3.1
     */
    @OnAwt
    public void setErrorTextFont(Font font) {
        dialogPanel.getErrorTextLabel().setFont(font);
    }

    /**
     * Sets the error text font color.
     * <p>
     * <h2>AWT Thread</h2>
     * Should be called in the AWT event dispatch thread.
     * </p>
     *
     * @param color
     *            the {@link Color}.
     * @since 3.1
     */
    @OnAwt
    public void setErrorTextFontColor(Color color) {
        dialogPanel.getErrorTextLabel().setForeground(color);
    }

    /**
     * @see #STATUS_PROPERTY
     */
    public void addVetoableChangeListener(VetoableChangeListener listener) {
        vetoableSupport.addVetoableChangeListener(listener);
    }

    /**
     * @see #STATUS_PROPERTY
     */
    public void removeVetoableChangeListener(VetoableChangeListener listener) {
        vetoableSupport.removeVetoableChangeListener(listener);
    }

    /**
     * @see #STATUS_PROPERTY
     */
    public void addVetoableChangeListener(String propertyName, VetoableChangeListener listener) {
        vetoableSupport.addVetoableChangeListener(propertyName, listener);
    }

    /**
     * @see #STATUS_PROPERTY
     */
    public void removeVetoableChangeListener(String propertyName, VetoableChangeListener listener) {
        vetoableSupport.removeVetoableChangeListener(propertyName, listener);
    }

    private void setupDialog() {
        dialog.add(getAWTComponent());
        dialog.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        dialog.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                cancelAction.actionPerformed(null);
            }
        });
    }

    private void setupPanel() {
        notNull(fieldsPanel, "fieldsPanel");
        dialogPanel.add(fieldsPanel, "cell 0 0");
        dialogPanel.getErrorTextLabel().setIcon(new ImageIcon(emptyIcon.getImage()));
        dialogPanel.getErrorTextLabel().setText(" ");
    }

    private void setupActions() {
        approveAction.setDialog(this);
        approveAction.setTexts(texts);
        cancelAction.setDialog(this);
        cancelAction.setTexts(texts);
        getCancelButton().setAction(cancelAction);
        getApproveButton().setAction(approveAction);
        if (showApproveButton) {
            addApproveButton();
        } else {
            removeApproveButton();
        }
        if (showRestoreButton) {
            addRestoreButton();
        } else {
            removeRestoreButton();
        }
    }

    private void setupKeyboardActions() {
        JRootPane rootPane = dialog.getRootPane();
        rootPane.setDefaultButton(getApproveButton());
        rootPane.registerKeyboardAction(cancelAction, getKeyStroke(VK_ESCAPE, 0), WHEN_IN_FOCUSED_WINDOW);
    }

    private void addRestoreButton() {
        JButton restoreButton = getRestoreButton();
        restoreAction.setDialog(this);
        restoreAction.setTexts(texts);
        restoreButton.setVisible(true);
        restoreButton.setAction(restoreAction);
    }

    private void readdRestoreButton() {
        JButton cancelButton = getCancelButton();
        JButton restoreButton = getRestoreButton();
        JPanel buttonsPanel = dialogPanel.getButtonsPanel();
        restoreAction.setDialog(this);
        restoreAction.setTexts(texts);
        restoreButton.setVisible(true);
        restoreButton.setAction(restoreAction);
        buttonsPanel.remove(cancelButton);
        buttonsPanel.add(restoreButton);
        buttonsPanel.add(dialogPanel.getRestoreStrut());
        buttonsPanel.add(cancelButton);
        buttonsPanel.validate();
    }

    private void removeRestoreButton() {
        JPanel buttonsPanel = dialogPanel.getButtonsPanel();
        JButton restoreButton = getRestoreButton();
        buttonsPanel.remove(restoreButton);
        buttonsPanel.remove(dialogPanel.getRestoreStrut());
        restoreButton.setVisible(false);
        buttonsPanel.validate();
    }

    private void addApproveButton() {
        JButton approveButton = getApproveButton();
        approveAction.setDialog(this);
        approveAction.setTexts(texts);
        approveButton.setVisible(true);
        approveButton.setAction(approveAction);
    }

    private void readdApproveButton() {
        JButton cancelButton = getCancelButton();
        JButton approveButton = getApproveButton();
        JPanel buttonsPanel = dialogPanel.getButtonsPanel();
        approveAction.setDialog(this);
        approveAction.setTexts(texts);
        approveButton.setVisible(true);
        approveButton.setAction(approveAction);
        buttonsPanel.remove(cancelButton);
        buttonsPanel.add(approveButton);
        buttonsPanel.add(dialogPanel.getApproveStrut());
        buttonsPanel.add(cancelButton);
        buttonsPanel.validate();
    }

    private void removeApproveButton() {
        JPanel buttonsPanel = dialogPanel.getButtonsPanel();
        JButton approveButton = getApproveButton();
        buttonsPanel.remove(approveButton);
        buttonsPanel.remove(dialogPanel.getApproveStrut());
        approveButton.setVisible(false);
        buttonsPanel.validate();
    }

    private void loadImages() {
        Locale locale = getLocale();
        this.emptyIcon = images.getResource("empty_icon", locale, IconSize.SMALL);
        this.errorIcon = images.getResource("error_icon", locale, IconSize.SMALL);
    }

}