Java tutorial
/** * EnderInstaller - Installer for the EnderPack Modpack. * Copyright (C) 2012, EnderVille.com * * 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 3 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, see <http://www.gnu.org/licenses/>. */ package com.enderville.enderinstaller.ui; import com.jidesoft.swing.CheckBoxTree; import com.jidesoft.swing.CheckBoxTreeSelectionModel; import java.awt.Desktop; import java.awt.Dimension; import java.awt.Image; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.io.*; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.imageio.ImageIO; import javax.swing.*; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import com.enderville.enderinstaller.util.InstallScript; import com.enderville.enderinstaller.util.InstallerConfig; import net.miginfocom.layout.AC; import net.miginfocom.layout.CC; import net.miginfocom.layout.LC; import net.miginfocom.swing.MigLayout; import org.apache.commons.io.FilenameUtils; import org.apache.log4j.Logger; /** * The main GUI used to install mods. */ @SuppressWarnings("serial") public class Installer extends JFrame implements ActionListener, TreeSelectionListener, HyperlinkListener { private static final Logger LOGGER = Logger.getLogger(Installer.class); private static final String PRESET_NONE = "Minimum Install"; private static final String PRESET_ALL = "Full Install"; private static final String PRESET_CUSTOM = "Custom"; private JButton nextButton; private JButton cancelButton; private JProgressBar progressBar; private int step = 0; private JPanel contentPane; private CheckBoxTree modTree; private JComboBox presetDropdown; private JPanel modDescrPane; private DefaultMutableTreeNode modTreeRoot; private JButton targetButton; private Map<String, List<String>> presetMap; private Map<String, DefaultMutableTreeNode> treeNodeMap; public Installer() { super(InstallerConfig.getFrameTitle()); SplashScreen splash = new SplashScreen(); splash.run(); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); setMinimumSize(new Dimension(800, 600)); setLocationRelativeTo(null); init(); pack(); } private void init() { JPanel p = new JPanel(); p.setLayout(new MigLayout("fill", "[fill,grow|]", "[fill,grow||]")); //lc col row p.add(getMainPane(), new CC().spanX().grow()); p.add(getProgressBar(), new CC().growX()); p.add(getCancelButton(), new CC().alignX("right")); p.add(getNextButton(), new CC().alignX("right")); setContentPane(p); Image bg = Toolkit.getDefaultToolkit().getImage(Installer.class.getResource("/wiki.png")); this.setIconImage(bg); } private JButton getNextButton() { if (nextButton == null) { nextButton = new JButton("Next"); nextButton.addActionListener(this); } return nextButton; } private JButton getCancelButton() { if (cancelButton == null) { cancelButton = new JButton("Cancel"); cancelButton.addActionListener(this); } return cancelButton; } private JProgressBar getProgressBar() { if (progressBar == null) { progressBar = new JProgressBar(); } return progressBar; } private JPanel getMainPane() { if (contentPane == null) { contentPane = new JPanel(); initialPanel(contentPane); } return contentPane; } protected void initialPanel(JPanel contentPane) { JLabel text = new JLabel(); try { ImageIcon icon = new ImageIcon(ImageIO.read(new FileInputStream(InstallerConfig.getLogoFile()))); text.setIcon(icon); } catch (IOException e) { LOGGER.error("IO error on logo.png", e); } StringBuilder textBuffer = new StringBuilder(); try { BufferedReader r = new BufferedReader(new FileReader(InstallerConfig.getInitTextFile())); String line = null; while ((line = r.readLine()) != null) { textBuffer.append(line + "\n"); } } catch (IOException ioe) { LOGGER.error("IO error on logo.png", ioe); } text.setText(textBuffer.toString()); text.setVerticalTextPosition(JLabel.BOTTOM); text.setHorizontalTextPosition(JLabel.CENTER); contentPane.setLayout(new MigLayout(new LC().fill())); contentPane.add(text, new CC().alignX("center").wrap()); contentPane.add(getTargetButton(), new CC().alignX("center").wrap()); } private JButton getTargetButton() { if (targetButton == null) { targetButton = new JButton("Set Target Minecraft / MultiMC instance Folder..."); targetButton.addActionListener(this); } return targetButton; } private void chooseTargetMinecraftFolder() { JFileChooser chooser = new JFileChooser(); chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); chooser.setMultiSelectionEnabled(false); int opt = chooser.showOpenDialog(getMainPane()); if (opt == JFileChooser.APPROVE_OPTION) { File dir = chooser.getSelectedFile(); String oldDir = InstallerConfig.getMinecraftFolder(); InstallerConfig.setMinecraftFolder(dir.getAbsolutePath()); File mcjar = new File(InstallerConfig.getMinecraftJar()); if (!mcjar.exists()) { JOptionPane.showMessageDialog(getMainPane(), "The installer couldn't find a minecraft installation in the specified folder.\n" + "Restoring minecraft folder to " + oldDir, "Error setting target Minecraft installation", JOptionPane.ERROR_MESSAGE); InstallerConfig.setMinecraftFolder(oldDir); } } } private void advanceStep() { step++; switch (step) { case 1: { String msg = InstallScript.preInstallCheck(); if (msg != null) { //TODO maybe a better way to check for collisions // so that non-conflicting installs doesn't raise this warning msg = msg + "\nThe EnderPack is meant to be installed on a fresh minecraft.jar and mods folder.\n" + "Do you wish to continue, and attempt to install on top of the current minecraft?"; int opt = JOptionPane.showConfirmDialog(this, msg, "Installation Warning", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); if (opt == JOptionPane.NO_OPTION) { setVisible(false); dispose(); } } File extraFolder = new File(InstallerConfig.getExtraModsFolder()); //Check that the extras folder exists and that it has some mods in it. Otherwise, simply start installing if (extraFolder.exists()) { File[] children = extraFolder.listFiles(); if (children != null) { for (File child : children) { if (child.isDirectory()) { buildOptionsPane(); return; } } } //extras folder was empty, or no mod directories (only files) advanceStep(); } else { //extras folder didn't exist. advanceStep(); } return; } case 2: buildInstallingPane(); return; default: LOGGER.error("Advanced too far"); } } private void buildInstallingPane() { getNextButton().setEnabled(false); getCancelButton().setEnabled(false); getMainPane().removeAll(); getMainPane().setLayout(new MigLayout(new LC().fill())); final JTextArea text = new JTextArea(); text.setEditable(false); getMainPane().add(new JScrollPane(text), new CC().grow().spanY().wrap()); getMainPane().validate(); getMainPane().repaint(); SwingWorker<Object, String> worker = new SwingWorker<Object, String>() { @Override protected Object doInBackground() throws Exception { try { List<String> mods = new ArrayList<String>(); CheckBoxTreeSelectionModel select = getModTree().getCheckBoxTreeSelectionModel(); TreePath[] paths = select.getSelectionPaths(); if (paths != null && paths.length > 0) { for (TreePath path : paths) { DefaultMutableTreeNode node = ((DefaultMutableTreeNode) path.getLastPathComponent()); String mod = (String) node.getUserObject(); if (mod == null) { for (int i = 0; i < node.getChildCount(); i++) { DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i); mods.add((String) child.getUserObject()); } } else { mods.add(mod); } } } InstallScript.guiInstall(mods, text, getProgressBar()); } catch (Exception e) { LOGGER.error("Error while trying to install mods", e); JOptionPane.showMessageDialog(Installer.this, "Unexpected error while installing mods:\n" + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); setVisible(false); dispose(); } return null; } @Override public void done() { getNextButton().removeActionListener(Installer.this); getNextButton().setText("Done"); getNextButton().addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { setVisible(false); dispose(); } }); getNextButton().setEnabled(true); } }; worker.execute(); } private CheckBoxTree getModTree() { if (modTree == null) { modTreeRoot = new DefaultMutableTreeNode(); treeNodeMap = new HashMap<String, DefaultMutableTreeNode>(); try { File opt = new File(InstallerConfig.getExtraModsFolder()); for (File mod : opt.listFiles()) { if (!mod.isDirectory()) { continue; } String modName = mod.getName(); DefaultMutableTreeNode child = new DefaultMutableTreeNode(modName); treeNodeMap.put(modName, child); modTreeRoot.add(child); } modTree = new CheckBoxTree(modTreeRoot); modTree.setRootVisible(false); modTree.getSelectionModel().addTreeSelectionListener(this); modTree.getCheckBoxTreeSelectionModel().addTreeSelectionListener(this); } catch (Exception e) { LOGGER.error("Error scanning and building optional mod tree.", e); JOptionPane.showMessageDialog(this, "Error scanning mods folder. It may not exists.", "Error", JOptionPane.ERROR_MESSAGE); setVisible(false); dispose(); return null; } } return modTree; } private void buildOptionsPane() { JPanel p = getMainPane(); p.removeAll(); JPanel leftPane = new JPanel(new MigLayout(new LC().fill(), new AC().index(1).fill().grow(), new AC().index(0).fill().grow().index(1).fill().grow())); leftPane.add(new JLabel("Please select which optional mods you would like to install."), new CC().spanX().wrap()); leftPane.add(new JLabel("Presets: "), new CC()); leftPane.add(getPresetDropdown(), new CC().grow().wrap()); leftPane.add(new JScrollPane(getModTree()), new CC().spanX().spanY().grow().wrap()); p.setLayout(new MigLayout(new LC().fill(), new AC().index(0).fill().grow().index(1).fill().grow(), new AC().fill())); p.add(leftPane, new CC().grow().width(":300:")); p.add(getModDescriptionPane(), new CC().grow().width(":300:300").wrap()); p.validate(); p.repaint(); } private JComboBox getPresetDropdown() { if (presetDropdown == null) { presetDropdown = new JComboBox(); presetDropdown.addItem(PRESET_NONE); presetDropdown.addItem(PRESET_ALL); presetDropdown.addItem(PRESET_CUSTOM); presetDropdown.addActionListener(this); File presetDir = new File(FilenameUtils.concat(InstallerConfig.getInstallerDir(), "config/presets")); if (!presetDir.exists() || !presetDir.isDirectory()) { LOGGER.warn("presets does not exist or is not a directory"); } else { presetMap = new HashMap<String, List<String>>(); File[] children = presetDir.listFiles(); for (File child : children) { if (!child.isFile()) { continue; } String name = FilenameUtils.getBaseName(child.getName()); try { BufferedReader r = new BufferedReader(new FileReader(child)); String l = null; List<String> mods = new ArrayList<String>(); while ((l = r.readLine()) != null) { mods.add(l); } presetMap.put(name, mods); presetDropdown.addItem(name); } catch (IOException e) { LOGGER.warn("Error reading " + child.getName(), e); } } } } return presetDropdown; } private JPanel getModDescriptionPane() { if (modDescrPane == null) { modDescrPane = new JPanel(); modDescrPane.add(new JLabel("<html>This is the mod description panel.<br>" + "Select an optional mod to the left<br>" + "to view a description of it.</html>")); } return modDescrPane; } private void loadModDescription(String modName) { JPanel p = getModDescriptionPane(); p.removeAll(); p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); final String extras = InstallerConfig.getExtraModsFolder(); final String modFolderName = FilenameUtils.concat(extras, modName); File modFolder = new File(modFolderName); if (!modFolder.exists()) { LOGGER.error("Mod folder for " + modName + " does not exist."); } File descrFile = new File(FilenameUtils.concat(modFolderName, "description.txt")); File imgFile = new File(FilenameUtils.concat(modFolderName, "image.png")); if (!descrFile.exists() && !imgFile.exists()) { p.add(new JLabel("<html>No description for:<br>" + modName + "</html>")); } else { if (imgFile.exists()) { try { JLabel label = new JLabel(); BufferedImage img = ImageIO.read(imgFile); label.setIcon(new ImageIcon(img)); p.add(label); } catch (IOException e) { LOGGER.error("Error reading image file: " + imgFile.getPath(), e); } } if (descrFile.exists()) { StringBuilder buffer = new StringBuilder(); try { BufferedReader r = new BufferedReader(new FileReader(descrFile)); String l = null; while ((l = r.readLine()) != null) { buffer.append(l + "\n"); } r.close(); JEditorPane area = new JEditorPane(); area.setContentType("text/html"); area.setText(buffer.toString()); area.setEditable(false); area.addHyperlinkListener(this); area.setCaretPosition(0); p.add(new JScrollPane(area)); } catch (IOException e) { LOGGER.error("Error reading description file: " + descrFile.getPath(), e); } } } p.validate(); p.repaint(); } private void loadPreset(String presetName) { if (presetName.equals(PRESET_NONE)) { getModTree().getCheckBoxTreeSelectionModel().clearSelection(); } else if (presetName.equals(PRESET_ALL)) { CheckBoxTreeSelectionModel selectModel = getModTree().getCheckBoxTreeSelectionModel(); selectModel.setSelectionPath(new TreePath(modTreeRoot)); } else { loadPresetFromFile(presetName); } } private void loadPresetFromFile(String presetName) { List<String> mods = presetMap.get(presetName); CheckBoxTreeSelectionModel select = getModTree().getCheckBoxTreeSelectionModel(); select.clearSelection(); TreePath[] paths = new TreePath[mods.size()]; for (int i = 0; i < mods.size(); i++) { String mod = mods.get(i); DefaultMutableTreeNode modNode = treeNodeMap.get(mod); paths[i] = new TreePath(new Object[] { modTreeRoot, modNode }); } select.setSelectionPaths(paths); presetDropdown.setSelectedItem(presetName); } @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == getNextButton()) { advanceStep(); } else if (e.getSource() == getCancelButton()) { setVisible(false); dispose(); } else if (e.getSource() == getPresetDropdown()) { loadPreset(getPresetDropdown().getSelectedItem().toString()); } else if (e.getSource() == getTargetButton()) { chooseTargetMinecraftFolder(); } } @Override public void valueChanged(TreeSelectionEvent e) { if (!e.isAddedPath()) { return; } TreePath path = e.getPath(); CheckBoxTree tree = getModTree(); if (e.getSource() == tree.getSelectionModel()) { DefaultMutableTreeNode last = (DefaultMutableTreeNode) path.getLastPathComponent(); loadModDescription(last.getUserObject().toString()); } else if (e.getSource() == tree.getCheckBoxTreeSelectionModel()) { getPresetDropdown().setSelectedItem(PRESET_CUSTOM); } } @Override public void hyperlinkUpdate(final HyperlinkEvent e) { if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) { return; } SwingWorker<Object, Object> worker = new SwingWorker<Object, Object>() { @Override protected Object doInBackground() throws Exception { try { String url = e.getURL().toExternalForm(); Desktop.getDesktop().browse(URI.create(url)); } catch (Exception e) { e.printStackTrace(); } return null; } }; worker.execute(); } public static boolean sanityCheck() { //Check for some required stuff File mcFolder = new File(InstallerConfig.getMinecraftFolder()); String errmsg = ""; if (InstallerConfig.getInstallerDir().toLowerCase().endsWith(".zip")) { //TODO is this the right check? errmsg += "You must extract the contents of the zip file to a folder before running the installer."; return false; } // if (!mcFolder.exists()) { // errmsg += InstallerConfig.getMinecraftFolder() + " doesn't exist.\n"; // } File modsFolder = new File(InstallerConfig.getInstallerModsFolder()); if (!modsFolder.exists()) { errmsg += InstallerConfig.getInstallerModsFolder() + " doesn't exist.\n"; } File logofile = new File(InstallerConfig.getInitTextFile()); if (!logofile.exists()) { errmsg += InstallerConfig.getInitTextFile() + " doesn't exist.\n"; } File textfile = new File(InstallerConfig.getLogoFile()); if (!textfile.exists()) { errmsg += InstallerConfig.getLogoFile() + " doesn't exist.\n"; } errmsg = errmsg.trim(); if (!errmsg.isEmpty()) { LOGGER.error(errmsg); JOptionPane.showMessageDialog(null, errmsg, "Installer Error", JOptionPane.ERROR_MESSAGE); } return errmsg.isEmpty(); } public static void main(String[] args) throws IOException { LOGGER.debug("Starting EnderPack Installer"); LOGGER.debug("Current OS: " + InstallerConfig.currentOS); if (!sanityCheck()) { return; } try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { LOGGER.warn("Couldn't set the look and feel", e); } Installer installer = new Installer(); installer.setVisible(true); } }