package wizard; import install.PrepareInstall; import install.Main; import install.OutputConsole; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; import java.util.Set; import javax.swing.AbstractAction; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JSeparator; import javax.swing.border.EmptyBorder; /** * This class implements a basic wizard dialog, where the programmer can * insert one or more Components to act as panels. These panels can be navigated * through arbitrarily using the 'Next' or 'Back' buttons, or the dialog itself * can be closed using the 'Cancel' button. Note that even though the dialog * uses a CardLayout manager, the order of the panels is not linear. Each panel * determines at runtime what its next and previous panel will be. ** * @author Graham Alvare * @author Dale Hamel * @author Brian Fristensky */ public class BIRCHWizard extends JFrame { // The image icons used for the buttons /** * The icon to display for the "Back" button */ public static final Icon BACK_ICON = new ImageIcon((URL) BIRCHWizard.class.getResource("backIcon.gif")); /** * The icon to display for the "Next" button */ public static final Icon NEXT_ICON = new ImageIcon((URL) BIRCHWizard.class.getResource("nextIcon.gif")); /** * The icon to display for the "Finish" button */ public static final Icon FINISH_ICON = new ImageIcon((URL) BIRCHWizard.class.getResource("finishIcon.gif")); /** * The icon to display for the "Cancel" button */ public static final Icon CANCEL_ICON = new ImageIcon((URL) BIRCHWizard.class.getResource("cancelIcon.gif")); /** * An action listener which calls setFinish(true). */ public final ActionListener changeNextToFinishAction = new ActionListener() { /** * Calls setFinish(true). ** * @param e ignored */ public void actionPerformed(ActionEvent e) { System.out.println("changing next to finish!"); setFinish(true); } }; /** * An action listener which calls setFinish(false). */ public final ActionListener changeFinishToNextAction = new ActionListener() { /** * Calls setFinish(true). ** * @param e ignored */ public void actionPerformed(ActionEvent e) { System.out.println("changing finish to next!"); setFinish(false); } }; // initialize panel components /** * Displays the "Start Panel" - the first panel of the installation wizard, * where the user selects whether they want to install BIRCH, or update * an existing BIRCH installation. */ private StartPanel startPanel = new StartPanel(this); /** * Displays the installation type selection panel - on this panel, the user * selects whether they want to install BIRCH using the default settings * or install BIRCH using their own custom settings * - i.e. default vs. advanced installation type */ private InstallTypePanel installType = new InstallTypePanel(this); /** * Displays the advanced installation panel - on this panel, the user can * specify any advanced options for the installation procedure, such as * selecting alternative binaries to download and install, or selecting * a development version of BIRCH. */ private AdvancedPanel advancedPanel = new AdvancedPanel(); /** * The card panel container to display the contents of the different install * step panels. What does this mean? Basically, this panel is a container * which will either display "startPanel", "installType", or * "advancedPanel". Using this card panel as a container allows consistent * formatting, because each install step panel ("startPanel", "installType", * and "advancedPanel") will be displayed inside of cardPanel; and, * cardPanel will be formatted to fit a certain way within the * GetBIRCH installer window. */ private JPanel cardPanel = new JPanel(); /** * The index of where we are within the install process. This is an index * of the panel array "panels". This index is necessary because of the * "Next" and "Previous" buttons. */ private int currentPanel = 0; /** * The master array of install panels. This array stores all of the install * step panels in their proper order. This array may be used to determine * which step panel should be displayed when either the "Next" or "Previous" * buttons are clicked within the GetBIRCH install window. */ private final WizardPanel panels [] = { startPanel, installType, advancedPanel }; /** * Determines whether the installer should display the "Next" or "Finish" * button. If this variable is set to true, the installer will display * the "Finish" button in place of the "Next" button. Just as a note, * both button objects are the same, only the text changes. This is an * important feature because the GetBIRCH install process may finish early * depending on which options the user picks. For instance, if the user * has selected a default install, we do not want to display the advanced * install panel, and thus, we may display the "Finish" button, and finish * the install process. Conversely, if the user has selected an advanced * install, we will want to display the advanced install panel. * * The value of finished may be set by the method setFinish() */ private boolean finish = false; /** * An action object to handle the functionality of the "Back"/"Previous" * button. This button, when clicked, moves the user backwards through the * install process. This action must decrement the currentPanel counter * (EXCEPT if it is already zero), and change the install step panel * corresponding to the new position (indicated by currentPanel) within the * panels array. This method must also disable the "Back" button, if we * have reached the first step of the installation process (if currentPanel * is set to zero). This method also calls the cardPanel's doLayout, * validate, and repaint methods to ensure that the cardPanel updates itself * properly (without calling these methods, we can change the current panel, * but it may not display properly). */ private JButton backButton = new JButton(new AbstractAction("Back", BACK_ICON) { /** * The business method of the back button. Please see the description * above (the description for backButton) for more details. ** * @param e ignored by this method. */ public void actionPerformed(ActionEvent e) { if (currentPanel > 0) { // decrement the currentPanel counter and change the current // install step panel displayed to the panel for the // previous install step currentPanel--; cardPanel.removeAll(); cardPanel.add(panels[currentPanel], "CURRENT_PANEL" + currentPanel); // ensure that card panel displays the install step panel // properly, by forcefully updating the panel cardPanel.doLayout(); cardPanel.validate(); cardPanel.repaint(50L); } // disable the back button if we are at the first install step panel // for the entire install process backButton.setEnabled(currentPanel > 0); // this step is likely useless, but is included for good measure // update the finish status for the current panel (i.e. if the // install process can be completed after the current panel replace // the "Next" button with the "Finish" button. setFinish(panels[currentPanel].isFinished()); } }) {{ setEnabled(false); }}; /** * An action object to handle the functionality of the "Next"/"Finish" * button. This button, when clicked, either moves the user forwards * through the install process, or completes the install process, by calling * all relevant methods. If we are not finished the install process, * to proceed to the next step of the install process, we must increment * the currentPanel counter, and change the install step panel corresponding * to the new position (indicated by currentPanel) within the panels array. * This method must also change the "Next" button to "Finish", if by * completing the next panel the user will be able to complete the * installation process. This method also calls the cardPanel's * doLayout, validate, and repaint methods to ensure that the cardPanel * updates itself properly (without calling these methods, we can change * the current panel, but it may not display properly). */ private JButton nextButton = new JButton(new AbstractAction("Next", NEXT_ICON) { /** * The business method of the next button. Please see the description * above (the description for nextButton) for more details. ** * @param e ignored by this method. */ public void actionPerformed(ActionEvent e) { currentPanel++; ////////////////// // branch between the next and finish functionality ////////////////// // 1. if the "if" statement evaluates true, we will perform the // "Next" button functionality - move the install process forward // to the next install step panel // // 2. if the "if" statement evaluates false, the else block will // perform the "Finish" button functionality - complete the // install process using the options which the user has already // selected // ////////////////// // the criteria to decide whether option 1 or 2 is followed are: ////////////////// // A. are we at the final panel in the installation process // // B. is the "finish" variable set (i.e. has an option been selected // on the current panel that indicates no further input from the // user is necessary to complete the installation process). if (currentPanel < panels.length && !finish) { /** * Move to the next step of the installation */ cardPanel.removeAll(); cardPanel.add(panels[currentPanel], "CURRENT_PANEL" + currentPanel); cardPanel.doLayout(); cardPanel.validate(); cardPanel.repaint(50L); backButton.setEnabled(true); // DISPLAY THE INSTALL INFORMATION FROM THE ADVANCED PANEL // ON THE INSTALL TYPE PANEL String installInfo; String[] rawInfo; rawInfo = advancedPanel.getInstallInfo(); installInfo = ""; for (int i = 0; i < rawInfo.length; i++) { installInfo = installInfo + rawInfo[i] + "\n"; } installType.installStatusText.setText(installInfo + "Version: " + Main.VERSION + "\n"); setFinish(panels[currentPanel].isFinished()); } else { /** * Begin the installation process for BIRCH with the given installation * parameters from all panels in the wizard process. */ final File logDir = new File(advancedPanel.getLogDir()); FileOutputStream logger = null; if (logDir != null) { File logfile = new File(logDir, "getbirch.log"); try { if ((logfile.exists() && logfile.canWrite()) || logfile.createNewFile()) { logger = new FileOutputStream(logfile); } else { System.err.println("ERROR CREATING LOG FILE!"); System.exit(0); } } catch (IOException ioe) { ioe.printStackTrace(System.err); System.err.println("ERROR CREATING LOG FILE!"); System.exit(0); } } final OutputConsole console = new OutputConsole(logger); // start a new thread to conduct the install process // the new thread will communicate with the OutputConsole // object, "console", in the current thread new Thread(new Runnable() { public void run() { // obtain all of the information needed to proceed with the install final boolean install = startPanel.isInstall(); final boolean advanced = installType.isAdvancedInstall(); final boolean createBackup = startPanel.makeBackup(); final boolean devel = advancedPanel.isDevelopment(); final boolean mini = advancedPanel.isMiniBIRCH(); final File installDir = new File((install ? advancedPanel.getInstallDir() : startPanel.getUpdateDir())); final String frameType = advancedPanel.getFrameworkVersion(); final Set binaries = advancedPanel.getBinariesSelected(); String adminEmail; // if this is an installation, and not an update, // obtain the administrator's email address from the appropriate panel if (install) { adminEmail = startPanel.getEmail(); } else { adminEmail = ""; } // display in the log whether this is an advanced // or default installation process if (advanced) { System.out.println("Proceeding with default install"); } else { System.out.println("An advanced install was selected"); } // call the main installation process PrepareInstall.installBIRCH(install, installDir, frameType, binaries, devel, mini, adminEmail, createBackup, console); // ensure that the console will no longer cancel the // installation after the installation is complete console.finished(); } }).start(); // close the installation wizard window dispose(); } } }); /** * Cancels the BIRCH install wizard. */ private JButton cancelButton = new JButton(new AbstractAction("Cancel", CANCEL_ICON) { public synchronized void actionPerformed(ActionEvent e) { // cancels the GetBIRCH wizard Main.cancel(); } }); /** * Default constructor. This method creates a new WizardModel object and passes it * into the overloaded constructor. */ public BIRCHWizard() { super("GetBIRCH BIRCH Install Wizard"); getContentPane().setLayout(new BorderLayout()); // Create the outer wizard panel, which is responsible for three buttons: // Next, Back, and Cancel. It is also responsible a JPanel above them that // uses a CardLayout layout manager to display multiple panels in the // same spot. JPanel buttonPanel = new JPanel(); JSeparator separator = new JSeparator(); Box buttonBox = new Box(BoxLayout.X_AXIS); cardPanel.setBorder(new EmptyBorder(new Insets(5, 10, 5, 10))); cardPanel.setLayout(new CardLayout()); cardPanel.add(startPanel, "START_PANEL"); // Create the buttons with a separator above them, then place them // on the east side of the panel with a small amount of space between // the back and the next button, and a larger amount of space between // the next button and the cancel button. buttonPanel.setLayout(new BorderLayout()); buttonPanel.add(separator, BorderLayout.NORTH); buttonBox.setBorder(new EmptyBorder(new Insets(5, 10, 5, 10))); buttonBox.add(backButton); buttonBox.add(Box.createHorizontalStrut(10)); buttonBox.add(nextButton); buttonBox.add(Box.createHorizontalStrut(30)); buttonBox.add(cancelButton); buttonPanel.add(buttonBox, java.awt.BorderLayout.EAST); getContentPane().add(buttonPanel, java.awt.BorderLayout.SOUTH); getContentPane().add(cardPanel, java.awt.BorderLayout.CENTER); // be sure to display a message and shutdown appropriately if the // output console window is closed addWindowListener(new WindowAdapter() { @Override public synchronized void windowClosing(WindowEvent arg0) { Main.cancel(); } }); pack(); setSize(650,450); setVisible(true); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); } /** * This changes the "Next" button in the wizard into a "Finished" button, * if the parameter "finish" is true, or vice-versa if "finish" is false. * Please do not confuse the parameter "finish" with the object variable * "finish". The object variable "finish" (this.finish) is a private * variable within the object, while the parameter "finish" is the * parameter supplied to this function when it is called. * * See the object variable finish for more details. ** * @param finish the status to set this.finish to */ public void setFinish(boolean finish) { this.finish = finish; // update the text and icon of the next button to reflect // its current functionality based on the parameter "finish" if (finish) { nextButton.setText("Finish"); nextButton.setIcon(FINISH_ICON); } else { nextButton.setText("Next"); nextButton.setIcon(NEXT_ICON); } } }