org.formic.wizard.impl.console.ConsoleWizard.java Source code

Java tutorial

Introduction

Here is the source code for org.formic.wizard.impl.console.ConsoleWizard.java

Source

/**
 * Formic installer framework.
 * Copyright (C) 2005 - 2011  Eric Van Dewoestine
 *
 * This library 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 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package org.formic.wizard.impl.console;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import charva.awt.BorderLayout;
import charva.awt.Component;
import charva.awt.Dimension;
import charva.awt.FlowLayout;
import charva.awt.IllegalComponentStateException;
import charva.awt.Toolkit;

import charva.awt.event.WindowAdapter;
import charva.awt.event.WindowEvent;

import charvax.swing.BorderFactory;
import charvax.swing.JButton;
import charvax.swing.JFrame;
import charvax.swing.JLabel;
import charvax.swing.JPanel;
import charvax.swing.JProgressBar;
import charvax.swing.JScrollPane;
import charvax.swing.JSeparator;
import charvax.swing.JTextArea;
import charvax.swing.SwingUtilities;

import edu.emory.mathcs.backport.java.util.concurrent.Semaphore;

import org.apache.commons.lang.WordUtils;

import org.formic.Installer;

import org.formic.util.dialog.console.ConsoleDialogs;

import org.formic.wizard.Wizard;
import org.formic.wizard.WizardStep;

import org.formic.wizard.impl.models.MultiPathModel;

import org.pietschy.wizard.WizardModel;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Wizard for console installers.
 *
 * @author Eric Van Dewoestine(ervandew@yahoo.com)
 */
public class ConsoleWizard implements Wizard, PropertyChangeListener {
    private static final Logger logger = LoggerFactory.getLogger(ConsoleWizard.class);

    private static final String BUSY_TEXT = Installer.getString("busy.text");

    private static JFrame frame;

    private Semaphore semaphore = new Semaphore(1);

    private WizardModel model;
    private org.pietschy.wizard.WizardStep activeStep;

    private boolean canceled;

    private JPanel mainPanel;
    private JPanel viewPanel;
    private JPanel hiddenPanel;

    private JButton previousButton;
    private JButton nextButton;
    //private JButton lastButton;
    private JButton finishButton;
    private JButton cancelButton;
    //private JButton closeButton;

    /**
     * Constructs a new instance.
     */
    public ConsoleWizard(WizardModel _model) {
        model = _model;
        model.addPropertyChangeListener(this);
        try {
            semaphore.acquire();
        } catch (Exception e) {
            logger.error("Error acquiring console wizard lock.", e);
        }
    }

    /**
     * {@inheritDoc}
     * @see org.formic.wizard.Wizard#showWizard(String)
     */
    public void showWizard(String action) {
        String error = null;

        Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
        int width = Installer.getDimension().width / 7;
        int height = Installer.getDimension().height / 12;

        if (screen.width < width) {
            error = Installer.getString("console.width.min", new Integer(screen.width), new Integer(width));
        }

        if (screen.height < height) {
            error = Installer.getString("console.height.min", new Integer(screen.height), new Integer(height));
        }

        Dimension dimension = new Dimension(width, height);
        // Below will screw up error dialog in some dimensions.
        /*Math.max(width, screen.width),
        Math.max(height, screen.height));*/

        frame = new JFrame(Installer.getString(action + ".title"));
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent _event) {
                canceled = true;
            }
        });

        viewPanel = new JPanel(new BorderLayout());

        mainPanel = new JPanel(new BorderLayout());
        mainPanel.add(createInfoPanel(), BorderLayout.NORTH);
        mainPanel.add(createButtonPanel(), BorderLayout.SOUTH);
        if (error == null) {
            mainPanel.add(viewPanel, BorderLayout.CENTER);
            frame.setSize(dimension);
        } else {
            frame.setSize(screen);
        }

        frame.add(mainPanel);
        frame.setVisible(true);

        if (error != null) {
            logger.error(error);
            ConsoleDialogs.showError(error);
            close(true);
        } else {
            model.reset();
        }
    }

    /**
     * Creates the info panel on the installation wizard.
     *
     * @return The info panel.
     */
    private JPanel createInfoPanel() {
        JPanel panel = new JPanel(new BorderLayout());
        panel.setBorder(BorderFactory.createLineBorder(Toolkit.getDefaultForeground()));

        JLabel title = new JLabel("Title");
        JPanel titlePanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
        titlePanel.add(title);

        JLabel summary = new JLabel("Summary");
        JPanel summaryPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 5));
        summaryPanel.add(summary);

        panel.add(titlePanel, BorderLayout.NORTH);
        panel.add(summaryPanel, BorderLayout.CENTER);

        return panel;
    }

    /**
     * Creates the button panel on the installation wizard.
     *
     * @return The button panel.
     */
    private JPanel createButtonPanel() {
        JPanel panel = new JPanel(new BorderLayout());

        previousButton = new JButton(Installer.getString("previous.text"));
        previousButton.addActionListener(new PreviousAction(this));

        nextButton = new JButton(Installer.getString("next.text"));
        nextButton.addActionListener(new NextAction(this));

        finishButton = new JButton(Installer.getString("finish.text"));
        finishButton.addActionListener(new FinishAction(this));

        cancelButton = new JButton(Installer.getString("cancel.text"));
        cancelButton.addActionListener(new CancelAction(this));

        JPanel buttonBar = new JPanel(new FlowLayout(FlowLayout.RIGHT, 3, 3));
        buttonBar.add(previousButton);
        buttonBar.add(nextButton);
        buttonBar.add(finishButton);
        buttonBar.add(cancelButton);

        panel.add(new JSeparator(), BorderLayout.NORTH);
        panel.add(buttonBar, BorderLayout.CENTER);

        return panel;
    }

    /**
     * {@inheritDoc}
     * @see org.formic.wizard.Wizard#waitFor()
     */
    public void waitFor() {
        try {
            semaphore.acquire();
        } catch (Exception e) {
            logger.error("Error acquiring console wizard lock in waitFor.", e);
        }
    }

    /**
     * {@inheritDoc}
     * @see org.formic.wizard.Wizard#wasCanceled()
     */
    public boolean wasCanceled() {
        return canceled;
    }

    /**
     * Close the wizard.
     *
     * @param canceled true if the wizard was canceled.
     */
    public void close(boolean canceled) {
        this.canceled = canceled;
        //frame.setVisible(false);
        Toolkit.getDefaultToolkit().close();
        try {
            semaphore.release();
        } catch (Exception e) {
            logger.error("Error releasing console wizard lock", e);
        }
    }

    /**
     * Gets the model for this instance.
     *
     * @return The model.
     */
    public WizardModel getModel() {
        return this.model;
    }

    /**
     * Gets the frame for this instance.
     *
     * @return The frame.
     */
    public static JFrame getFrame() {
        return frame;
    }

    /**
     * {@inheritDoc}
     * @see PropertyChangeListener#propertyChange(PropertyChangeEvent)
     */
    public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getPropertyName().equals(Wizard.ACTIVE_STEP)) {
            MultiPathModel model = (MultiPathModel) getModel();
            org.pietschy.wizard.WizardStep step = model.getActiveStep();

            // update step listening.
            if (activeStep != null) {
                activeStep.removePropertyChangeListener(this);
            }
            activeStep = step;
            activeStep.addPropertyChangeListener(this);

            if (step != null) {
                updateView(((ConsoleWizardStep) activeStep).getConsoleView());

                WizardStep ws = ((ConsoleWizardStep) step).getStep();

                updateButtonStatus(model, ws, step);

                // notify step that it is displayed.
                ws.displayed();
            }
        } else if (evt.getPropertyName().equals(WizardStep.CANCEL)) {
            boolean cancelEnabled = ((Boolean) evt.getNewValue()).booleanValue();
            cancelButton.setEnabled(cancelEnabled);
        } else if (evt.getPropertyName().equals(WizardStep.PREVIOUS)) {
            boolean previousEnabled = ((Boolean) evt.getNewValue()).booleanValue();
            previousButton.setEnabled(previousEnabled);
        } else if (evt.getPropertyName().equals(WizardStep.VALID)
                || evt.getPropertyName().equals(WizardStep.BUSY)) {
            MultiPathModel model = (MultiPathModel) getModel();
            org.pietschy.wizard.WizardStep step = model.getActiveStep();
            final WizardStep ws = ((ConsoleWizardStep) step).getStep();

            boolean nextEnabled = ws.isValid() && !ws.isBusy() && !model.isLastStep(step);
            nextButton.setEnabled(nextEnabled);

            // show inifinite wait for busy state.
            if (evt.getPropertyName().equals(WizardStep.BUSY) && ws.isBusyAnimated()) {
                final boolean busy = ((Boolean) evt.getNewValue()).booleanValue();

                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        if (busy) {
                            hiddenPanel = (JPanel) viewPanel.getComponents()[0];
                            updateView(new BusyPanel(ws));
                        } else {
                            updateView(hiddenPanel);
                            hiddenPanel = null;
                        }
                    }
                });
            }
        }
    }

    /**
     * Update the view for the current step.
     */
    private void updateView(Component view) {
        viewPanel.add(view);
        Component[] components = viewPanel.getComponents();
        for (int ii = 0; ii < components.length; ii++) {
            if (components[ii] != view) {
                try {
                    viewPanel.remove(components[ii]);
                } catch (IllegalComponentStateException icse) {
                    mainPanel.getCurrentFocus();
                    viewPanel.remove(components[ii]);
                }
            }
        }
        viewPanel.validate();
        viewPanel.repaint();
    }

    /**
     * Update the state (enabled / disabled) of the buttons in the button bar.
     */
    private void updateButtonStatus(MultiPathModel model, WizardStep ws, org.pietschy.wizard.WizardStep step) {
        // set whether previous step is enabled or not.
        boolean previousAvailable = !model.isFirstStep(step) && !model.isLastStep(step) && ws.isPreviousEnabled();
        previousButton.setEnabled(previousAvailable);

        // set whether next step is enabled or not.
        boolean nextEnabled = ws.isValid() && !ws.isBusy() && !model.isLastStep(step);
        nextButton.setEnabled(nextEnabled);

        // set whether cancel step is enabled or not.
        boolean cancelAvailable = !model.isLastStep(step) && ws.isCancelEnabled();
        cancelButton.setEnabled(cancelAvailable);

        // set whether finish step is enabled or not.
        finishButton.setEnabled(model.isLastStep(step));
    }

    /**
     * Panel to be displayed when a busy step supports animation.
     */
    private class BusyPanel extends JPanel {
        /**
         * Constructs a new BusyPanel.
         *
         * @param ws The WizardStep.
         */
        public BusyPanel(WizardStep ws) {
            setLayout(new BorderLayout());

            String message = Installer.getStringOrDefault(ws.getName() + ".busy", BUSY_TEXT);
            JTextArea messageArea = new JTextArea(WordUtils.wrap(message, 50), 3, 50);
            messageArea.setEditable(false);

            JScrollPane messagePane = new JScrollPane(messageArea);
            JPanel centerMessage = new JPanel(new FlowLayout(FlowLayout.CENTER, 1, 1));
            centerMessage.add(messagePane);
            add(centerMessage, BorderLayout.NORTH);

            JProgressBar progress = new JProgressBar();
            progress.setStringPainted(true);
            progress.setIndeterminate(true);

            JPanel progressPanel = new JPanel(new BorderLayout());
            progressPanel.setBorder(BorderFactory.createLineBorder(null, 1));
            progressPanel.add(progress, BorderLayout.CENTER);
            progressPanel.setSize(25, 2);

            JPanel centerProgress = new JPanel(new FlowLayout(FlowLayout.CENTER, 1, 1));
            centerProgress.add(progressPanel);

            add(centerProgress, BorderLayout.CENTER);
        }
    }
}