/* * SK's Minecraft Launcher * Copyright (C) 2010-2014 Albert Pham <http://www.sk89q.com> and contributors * Please see LICENSE.txt for license information. */ package com.skcraft.launcher.dialog; import com.skcraft.concurrency.ObservableFuture; import com.skcraft.launcher.Instance; import com.skcraft.launcher.InstanceList; import com.skcraft.launcher.Launcher; import com.skcraft.launcher.launch.LaunchListener; import com.skcraft.launcher.launch.LaunchOptions; import com.skcraft.launcher.launch.LaunchOptions.UpdatePolicy; import com.skcraft.launcher.swing.*; import com.skcraft.launcher.util.SharedLocale; import com.skcraft.launcher.util.SwingExecutor; import lombok.Getter; import lombok.NonNull; import lombok.extern.java.Log; import net.miginfocom.swing.MigLayout; import javax.swing.*; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.lang.ref.WeakReference; import static com.skcraft.launcher.util.SharedLocale.tr; /** * The main launcher frame. */ @Log public class LauncherFrame extends JFrame { private final Launcher launcher; @Getter private final InstanceTable instancesTable = new InstanceTable(); private final InstanceTableModel instancesModel; @Getter private final JScrollPane instanceScroll = new JScrollPane(instancesTable); private WebpagePanel webView; private JSplitPane splitPane; private final JButton launchButton = new JButton(SharedLocale.tr("launcher.launch")); private final JButton refreshButton = new JButton(SharedLocale.tr("launcher.checkForUpdates")); private final JButton optionsButton = new JButton(SharedLocale.tr("launcher.options")); private final JButton selfUpdateButton = new JButton(SharedLocale.tr("launcher.updateLauncher")); private final JCheckBox updateCheck = new JCheckBox(SharedLocale.tr("launcher.downloadUpdates")); /** * Create a new frame. * * @param launcher the launcher */ public LauncherFrame(@NonNull Launcher launcher) { super(tr("launcher.title", launcher.getVersion())); this.launcher = launcher; instancesModel = new InstanceTableModel(launcher.getInstances()); setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); setMinimumSize(new Dimension(400, 300)); initComponents(); pack(); setLocationRelativeTo(null); SwingHelper.setFrameIcon(this, Launcher.class, "icon.png"); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { loadInstances(); } }); } private void initComponents() { JPanel container = createContainerPanel(); container.setLayout(new MigLayout("fill, insets dialog", "[][]push[][]", "[grow][]")); webView = createNewsPanel(); splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, instanceScroll, webView); selfUpdateButton.setVisible(launcher.getUpdateManager().getPendingUpdate()); launcher.getUpdateManager().addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals("pendingUpdate")) { selfUpdateButton.setVisible((Boolean) evt.getNewValue()); } } }); updateCheck.setSelected(true); instancesTable.setModel(instancesModel); launchButton.setFont(launchButton.getFont().deriveFont(Font.BOLD)); splitPane.setDividerLocation(200); splitPane.setDividerSize(4); splitPane.setOpaque(false); container.add(splitPane, "grow, wrap, span 5, gapbottom unrel, w null:680, h null:350"); SwingHelper.flattenJSplitPane(splitPane); container.add(refreshButton); container.add(updateCheck); container.add(selfUpdateButton); container.add(optionsButton); container.add(launchButton); add(container, BorderLayout.CENTER); instancesModel.addTableModelListener(new TableModelListener() { @Override public void tableChanged(TableModelEvent e) { if (instancesTable.getRowCount() > 0) { instancesTable.setRowSelectionInterval(0, 0); } } }); instancesTable.addMouseListener(new DoubleClickToButtonAdapter(launchButton)); refreshButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { loadInstances(); launcher.getUpdateManager().checkForUpdate(); webView.browse(launcher.getNewsURL(), false); } }); selfUpdateButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { launcher.getUpdateManager().performUpdate(LauncherFrame.this); } }); optionsButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { showOptions(); } }); launchButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { launch(); } }); instancesTable.addMouseListener(new PopupMouseAdapter() { @Override protected void showPopup(MouseEvent e) { int index = instancesTable.rowAtPoint(e.getPoint()); Instance selected = null; if (index >= 0) { instancesTable.setRowSelectionInterval(index, index); selected = launcher.getInstances().get(index); } popupInstanceMenu(e.getComponent(), e.getX(), e.getY(), selected); } }); } protected JPanel createContainerPanel() { return new JPanel(); } /** * Return the news panel. * * @return the news panel */ protected WebpagePanel createNewsPanel() { return WebpagePanel.forURL(launcher.getNewsURL(), false); } /** * Popup the menu for the instances. * * @param component the component * @param x mouse X * @param y mouse Y * @param selected the selected instance, possibly null */ private void popupInstanceMenu(Component component, int x, int y, final Instance selected) { JPopupMenu popup = new JPopupMenu(); JMenuItem menuItem; if (selected != null) { menuItem = new JMenuItem(!selected.isLocal() ? tr("instance.install") : tr("instance.launch")); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { launch(); } }); popup.add(menuItem); if (selected.isLocal()) { popup.addSeparator(); menuItem = new JMenuItem(SharedLocale.tr("instance.openFolder")); menuItem.addActionListener(ActionListeners.browseDir( LauncherFrame.this, selected.getContentDir(), true)); popup.add(menuItem); menuItem = new JMenuItem(SharedLocale.tr("instance.openSaves")); menuItem.addActionListener(ActionListeners.browseDir( LauncherFrame.this, new File(selected.getContentDir(), "saves"), true)); popup.add(menuItem); menuItem = new JMenuItem(SharedLocale.tr("instance.openResourcePacks")); menuItem.addActionListener(ActionListeners.browseDir( LauncherFrame.this, new File(selected.getContentDir(), "resourcepacks"), true)); popup.add(menuItem); menuItem = new JMenuItem(SharedLocale.tr("instance.openScreenshots")); menuItem.addActionListener(ActionListeners.browseDir( LauncherFrame.this, new File(selected.getContentDir(), "screenshots"), true)); popup.add(menuItem); menuItem = new JMenuItem(SharedLocale.tr("instance.copyAsPath")); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { File dir = selected.getContentDir(); dir.mkdirs(); SwingHelper.setClipboard(dir.getAbsolutePath()); } }); popup.add(menuItem); popup.addSeparator(); if (!selected.isUpdatePending()) { menuItem = new JMenuItem(SharedLocale.tr("instance.forceUpdate")); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { selected.setUpdatePending(true); launch(); instancesModel.update(); } }); popup.add(menuItem); } menuItem = new JMenuItem(SharedLocale.tr("instance.hardForceUpdate")); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { confirmHardUpdate(selected); } }); popup.add(menuItem); menuItem = new JMenuItem(SharedLocale.tr("instance.deleteFiles")); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { confirmDelete(selected); } }); popup.add(menuItem); } popup.addSeparator(); } menuItem = new JMenuItem(SharedLocale.tr("launcher.refreshList")); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { loadInstances(); } }); popup.add(menuItem); popup.show(component, x, y); } private void confirmDelete(Instance instance) { if (!SwingHelper.confirmDialog(this, tr("instance.confirmDelete", instance.getTitle()), SharedLocale.tr("confirmTitle"))) { return; } ObservableFuture<Instance> future = launcher.getInstanceTasks().delete(this, instance); // Update the list of instances after updating future.addListener(new Runnable() { @Override public void run() { loadInstances(); } }, SwingExecutor.INSTANCE); } private void confirmHardUpdate(Instance instance) { if (!SwingHelper.confirmDialog(this, SharedLocale.tr("instance.confirmHardUpdate"), SharedLocale.tr("confirmTitle"))) { return; } ObservableFuture<Instance> future = launcher.getInstanceTasks().hardUpdate(this, instance); // Update the list of instances after updating future.addListener(new Runnable() { @Override public void run() { launch(); instancesModel.update(); } }, SwingExecutor.INSTANCE); } private void loadInstances() { ObservableFuture<InstanceList> future = launcher.getInstanceTasks().reloadInstances(this); future.addListener(new Runnable() { @Override public void run() { instancesModel.update(); if (instancesTable.getRowCount() > 0) { instancesTable.setRowSelectionInterval(0, 0); } requestFocus(); } }, SwingExecutor.INSTANCE); ProgressDialog.showProgress(this, future, SharedLocale.tr("launcher.checkingTitle"), SharedLocale.tr("launcher.checkingStatus")); SwingHelper.addErrorDialogCallback(this, future); } private void showOptions() { ConfigurationDialog configDialog = new ConfigurationDialog(this, launcher); configDialog.setVisible(true); } private void launch() { boolean permitUpdate = updateCheck.isSelected(); Instance instance = launcher.getInstances().get(instancesTable.getSelectedRow()); LaunchOptions options = new LaunchOptions.Builder() .setInstance(instance) .setListener(new LaunchListenerImpl(this)) .setUpdatePolicy(permitUpdate ? UpdatePolicy.UPDATE_IF_SESSION_ONLINE : UpdatePolicy.NO_UPDATE) .setWindow(this) .build(); launcher.getLaunchSupervisor().launch(options); } private static class LaunchListenerImpl implements LaunchListener { private final WeakReference<LauncherFrame> frameRef; private final Launcher launcher; private LaunchListenerImpl(LauncherFrame frame) { this.frameRef = new WeakReference<LauncherFrame>(frame); this.launcher = frame.launcher; } @Override public void instancesUpdated() { LauncherFrame frame = frameRef.get(); if (frame != null) { frame.instancesModel.update(); } } @Override public void gameStarted() { LauncherFrame frame = frameRef.get(); if (frame != null) { frame.dispose(); } } @Override public void gameClosed() { launcher.showLauncherWindow(); } } }