package net.oni2.aeinstaller.gui; import java.awt.Desktop; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.ResourceBundle; import java.util.TreeMap; import java.util.TreeSet; import java.util.Vector; import javax.swing.AbstractAction; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; import javax.swing.JSplitPane; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.RowSorter; import javax.swing.SortOrder; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.filechooser.FileFilter; import javax.swing.table.TableRowSorter; import net.oni2.aeinstaller.AEInstaller2; import net.oni2.aeinstaller.backend.AppExecution; import net.oni2.aeinstaller.backend.Paths; import net.oni2.aeinstaller.backend.Settings; import net.oni2.aeinstaller.backend.Settings.Platform; import net.oni2.aeinstaller.backend.SizeFormatter; import net.oni2.aeinstaller.backend.depot.DepotCacheUpdateProgressListener; import net.oni2.aeinstaller.backend.depot.DepotManager; import net.oni2.aeinstaller.backend.mods.Mod; import net.oni2.aeinstaller.backend.mods.ModManager; import net.oni2.aeinstaller.backend.mods.Type; import net.oni2.aeinstaller.backend.mods.download.ModDownloader; import net.oni2.aeinstaller.backend.mods.download.ModDownloader.State; import net.oni2.aeinstaller.backend.mods.download.ModDownloaderListener; import net.oni2.aeinstaller.backend.oni.InstallProgressListener; import net.oni2.aeinstaller.backend.oni.Installer; import net.oni2.aeinstaller.backend.oni.OniSplit; import net.oni2.aeinstaller.gui.about.AboutDialog; import net.oni2.aeinstaller.gui.downloadwindow.Downloader; import net.oni2.aeinstaller.gui.modtable.DownloadSizeListener; import net.oni2.aeinstaller.gui.modtable.ModTableFilter; import net.oni2.aeinstaller.gui.modtable.ModTableModel; import net.oni2.aeinstaller.gui.settings.SettingsDialog; import org.javabuilders.BuildResult; import org.javabuilders.annotations.DoInBackground; import org.javabuilders.event.BackgroundEvent; import org.javabuilders.swing.SwingJavaBuilder; import org.simplericity.macify.eawt.ApplicationEvent; import org.simplericity.macify.eawt.ApplicationListener; /** * @author Christian Illy */ public class MainWin extends JFrame implements ApplicationListener, DownloadSizeListener { private static final long serialVersionUID = -4027395051382659650L; private ResourceBundle bundle = ResourceBundle.getBundle(getClass() .getName()); @SuppressWarnings("unused") private BuildResult result = SwingJavaBuilder.build(this, bundle); private JMenu mainMenu; private JMenu toolsMenu; private TreeSet toolsMenuItems = new TreeSet(); private JSplitPane contents; private JComboBox cmbModTypes; private JTable tblMods; private ModTableModel model; private TableRowSorter sorter; private JLabel lblDownloadSizeVal; private JLabel lblSubmitterVal; private JLabel lblCreatorVal; private JLabel lblTypesVal; private JLabel lblPlatformVal; private JLabel lblPackageNumberVal; private HTMLLinkLabel lblDescriptionVal; private JButton btnInstall; private TreeSet execUpdates = null; private enum EInstallResult { DONE, OFFLINE, INCOMPATIBLE }; private EInstallResult installDone = EInstallResult.DONE; /** * Constructor of main window. */ public MainWin() { this.setTitle(SwingJavaBuilder.getConfig().getResource("appname") + " - v" + SwingJavaBuilder.getConfig().getResource("appversion")); contents.setDividerLocation(400); if (Settings.getPlatform() == Platform.MACOS) { mainMenu.setVisible(false); } getRootPane().setDefaultButton(btnInstall); lblDownloadSizeVal.setText(SizeFormatter.format(0, 2)); } private void initModTypeBox() { cmbModTypes.removeAllItems(); TreeMap types = new TreeMap(); for (Type t : ModManager.getInstance().getTypesWithContent()) { types.put(t.getName(), t); } for (Type t : types.values()) { cmbModTypes.addItem(t); } cmbModTypes.setSelectedIndex(0); } private void initTable() { tblMods.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); tblMods.getSelectionModel().addListSelectionListener( new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { int viewRow = tblMods.getSelectedRow(); if (viewRow < 0) { modSelection(null); } else { int modelRow = tblMods .convertRowIndexToModel(viewRow); Mod mod = (Mod) model.getValueAt(modelRow, -1); modSelection(mod); } } }); tblMods.addMouseListener(new MouseAdapter() { private void common(MouseEvent e) { int r = tblMods.rowAtPoint(e.getPoint()); if (r >= 0 && r < tblMods.getRowCount()) tblMods.setRowSelectionInterval(r, r); else tblMods.clearSelection(); int rowindex = tblMods.getSelectedRow(); if (rowindex >= 0) { if (e.isPopupTrigger() && e.getComponent() instanceof JTable) { int modelRow = tblMods.convertRowIndexToModel(rowindex); final Mod mod = (Mod) model.getValueAt(modelRow, -1); if (mod.isLocalAvailable()) { JPopupMenu popup = new JPopupMenu(); JMenuItem openModFolder = new JMenuItem(bundle .getString("openModFolder.text")); openModFolder .addActionListener(new ActionListener() { @Override public void actionPerformed( ActionEvent arg0) { try { Desktop.getDesktop().open( mod.getLocalPath()); } catch (IOException e) { e.printStackTrace(); } } }); popup.add(openModFolder); popup.show(e.getComponent(), e.getX(), e.getY()); } } } } @Override public void mousePressed(MouseEvent e) { common(e); } @Override public void mouseReleased(MouseEvent e) { common(e); } }); // To get checkbox-cells with background of row ((JComponent) tblMods.getDefaultRenderer(Boolean.class)) .setOpaque(true); model = new ModTableModel(); model.addDownloadSizeListener(this); tblMods.setModel(model); sorter = new TableRowSorter(model); tblMods.setRowSorter(sorter); sorter.setRowFilter(new ModTableFilter(null)); List sortKeys = new ArrayList(); sortKeys.add(new RowSorter.SortKey(1, SortOrder.ASCENDING)); sorter.setSortKeys(sortKeys); for (int i = 0; i < model.getColumnCount(); i++) { model.setColumnConstraints(i, tblMods.getColumnModel().getColumn(i)); } } private void exit() { dispose(); System.exit(0); } private void saveLocalData() { Settings.getInstance().serializeToFile(); DepotManager.getInstance().saveToFile(Settings.getDepotCacheFilename()); } @DoInBackground(progressMessage = "updateDepot.title", cancelable = false, indeterminateProgress = false) private void execDepotUpdate(final BackgroundEvent evt) { if (!Settings.getInstance().isOfflineMode()) { try { DepotManager.getInstance().updateInformation(false, new DepotCacheUpdateProgressListener() { @Override public void cacheUpdateProgress(String stepName, int current, int total) { evt.setProgressEnd(total); evt.setProgressValue(current); evt.setProgressMessage(stepName); } }); } catch (Exception e) { e.printStackTrace(); } } ModManager.getInstance().init(); initTable(); initModTypeBox(); tblMods.setVisible(true); } @SuppressWarnings("unused") private void checkUpdates(Object evtSource) { if ((evtSource != this) || Settings.getInstance().get("notifyupdates", true)) { if (Settings.getInstance().isOfflineMode()) { if (evtSource != this) { JOptionPane.showMessageDialog(this, bundle.getString("offlineMode.text"), bundle.getString("offlineMode.title"), JOptionPane.WARNING_MESSAGE); } } else { TreeSet mods = ModManager.getInstance().getUpdatableMods(); TreeSet tools = ModManager.getInstance() .getUpdatableTools(); int size = 0; String strMods = ""; for (Mod m : mods) { size += m.getZipSize(); if (strMods.length() > 0) strMods += "
"; strMods += " - " + m.getName(); } String strTools = ""; for (Mod m : tools) { size += m.getZipSize(); if (strTools.length() > 0) strTools += "
"; strTools += " - " + m.getName(); } if (size > 0) { String message = ""; message += String.format( bundle.getString("updatesAvailable.text"), strMods, strTools, SizeFormatter.format(size, 3)); message += ""; int res = JOptionPane.showConfirmDialog(this, message, bundle.getString("updatesAvailable.title"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (res == JOptionPane.YES_OPTION) { execUpdates = new TreeSet(); execUpdates.addAll(mods); execUpdates.addAll(tools); } } } } } @SuppressWarnings("unused") private void doUpdate() { if (execUpdates != null) { Downloader dl = new Downloader(execUpdates); try { dl.setVisible(true); if (dl.isFinished()) { TreeSet installed = Installer.getInstalledTools(); TreeSet tools = new TreeSet(); for (Mod m : execUpdates) if (m.isTool() && installed.contains(m.getPackageNumber())) tools.add(m); if (tools.size() > 0) { Installer.installTools(tools); } } } finally { dl.dispose(); } } execUpdates = null; } @SuppressWarnings("unused") private void focus() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { toFront(); repaint(); } }); } private void showSettings() { new SettingsDialog().setVisible(true); } private void showAbout() { new AboutDialog().setVisible(true); } private JFileChooser getConfigOpenSaveDialog(boolean save) { JFileChooser fc = new JFileChooser(); fc.setCurrentDirectory(Paths.getEditionBasePath()); if (save) fc.setDialogType(JFileChooser.SAVE_DIALOG); else fc.setDialogType(JFileChooser.OPEN_DIALOG); fc.setFileSelectionMode(JFileChooser.FILES_ONLY); fc.setFileFilter(new FileFilter() { @Override public String getDescription() { return "XML files"; } @Override public boolean accept(File arg0) { return (arg0.isDirectory()) || (arg0.getName().toLowerCase().endsWith(".xml")); } }); fc.setMultiSelectionEnabled(false); return fc; } @SuppressWarnings("unused") private void loadConfig() { JFileChooser fc = getConfigOpenSaveDialog(false); int res = fc.showOpenDialog(this); if (res == JFileChooser.APPROVE_OPTION) { if (fc.getSelectedFile().exists()) model.reloadSelection(fc.getSelectedFile()); } } @SuppressWarnings("unused") private void saveConfig() { JFileChooser fc = getConfigOpenSaveDialog(true); int res = fc.showSaveDialog(this); if (res == JFileChooser.APPROVE_OPTION) { File f = fc.getSelectedFile(); if (!f.getName().endsWith(".xml")) f = new File(f.getParentFile(), f.getName() + ".xml"); ModManager.getInstance().saveModSelection(f, model.getSelectedMods()); } } @DoInBackground(progressMessage = "initializingEdition.title", cancelable = false, indeterminateProgress = false) private void reglobalize(final BackgroundEvent evt) { Installer.initializeEdition(new InstallProgressListener() { @Override public void installProgressUpdate(int done, int total, String step) { evt.setProgressEnd(total); evt.setProgressValue(done); evt.setProgressMessage(step); } }); } @SuppressWarnings("unused") private void tools() { // TODO: Open tools manager JOptionPane.showMessageDialog(this, "tools", "todo", JOptionPane.INFORMATION_MESSAGE); } @SuppressWarnings("unused") private void refreshToolsMenu() { for (JMenuItem i : toolsMenuItems) { toolsMenu.remove(i); } toolsMenuItems.clear(); for (Mod m : ModManager.getInstance().getInstalledTools()) { if (m.getExeFile() != null && m.getExeFile().exists()) { JMenuItem item = new JMenuItem(); final Vector params = new Vector(); params.add(m.getExeFile().getPath()); final File wd = m.getWorkingDir(); Icon ico = null; if (m.getIconFile() != null && m.getIconFile().exists()) { ico = new ImageIcon(m.getIconFile().getPath()); } else { URL icon = AEInstaller2.class .getResource("images/transparent.png"); ico = new ImageIcon(icon); } item.setAction(new AbstractAction(m.getName(), ico) { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { AppExecution.execute(params, wd); } }); toolsMenuItems.add(item); toolsMenu.add(item); } } } @SuppressWarnings("unused") private void revertSelection() { model.revertSelection(); } @DoInBackground(progressMessage = "mandatoryFiles.title", cancelable = false, indeterminateProgress = false) private void checkMandatoryFiles(final BackgroundEvent evt) { if (!Settings.getInstance().isOfflineMode()) { TreeSet mand = new TreeSet(); for (Mod m : ModManager.getInstance().getMandatoryTools()) { if (m.isNewerAvailable()) { mand.add(m); } } for (Mod m : ModManager.getInstance().getMandatoryMods()) { if (m.isNewerAvailable()) { mand.add(m); } } if (mand.size() > 0) { ModDownloader m = new ModDownloader(mand, new ModDownloaderListener() { @Override public void updateStatus(ModDownloader source, State state, int filesDown, int filesTotal, int bytesDown, int bytesTotal, int duration, int remaining, int speed) { evt.setProgressEnd(filesTotal); evt.setProgressValue(filesDown); } }); while (!m.isFinished()) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } evt.setProgressMessage(bundle .getString("mandatoryToolsInstall.title")); Installer .installTools(ModManager.getInstance().getMandatoryTools()); } } @DoInBackground(progressMessage = "installing.title", cancelable = false, indeterminateProgress = false) private void install(final BackgroundEvent evt) { TreeSet mods = new TreeSet(); mods.addAll(ModManager.getInstance().getMandatoryMods()); mods.addAll(model.getSelectedMods()); boolean instReady = false; installDone = EInstallResult.DONE; while (!instReady) { TreeSet toDownload = new TreeSet(); for (Mod m : mods) { if (!m.isLocalAvailable()) toDownload.add(m); } if (Settings.getInstance().isOfflineMode()) { installDone = EInstallResult.OFFLINE; break; } if (toDownload.size() > 0) { Downloader dl = new Downloader(toDownload); try { dl.setVisible(true); if (!dl.isFinished()) break; } finally { dl.dispose(); } } HashMap> dependencies = ModManager.getInstance() .checkDependencies(mods); if (dependencies.size() > 0) { System.out.println("Unmet dependencies: " + dependencies.toString()); for (Mod m : dependencies.keySet()) { for (Mod mDep : dependencies.get(m)) mods.add(mDep); } } else { HashMap> conflicts = ModManager.getInstance() .checkIncompabitilites(mods); if (conflicts.size() > 0) { installDone = EInstallResult.INCOMPATIBLE; System.err.println("Incompatible mods: " + conflicts.toString()); break; } else { instReady = true; } } } if (instReady) { TreeSet actuallyMods = new TreeSet(); TreeSet actuallyTools = new TreeSet(); for (Mod m : mods) { if (m.isTool()) actuallyTools.add(m); else actuallyMods.add(m); } if (actuallyTools.size() > 0) { Installer.installTools(actuallyTools); } Installer.install(actuallyMods, new InstallProgressListener() { @Override public void installProgressUpdate(int done, int total, String step) { evt.setProgressEnd(total); evt.setProgressValue(done); evt.setProgressMessage(step); } }); installDone = EInstallResult.DONE; } } @SuppressWarnings("unused") private void installDone() { switch (installDone) { case DONE: JOptionPane.showMessageDialog(this, bundle.getString("installDone.text"), bundle.getString("installDone.title"), JOptionPane.INFORMATION_MESSAGE); break; case OFFLINE: JOptionPane.showMessageDialog(this, bundle.getString("offlineMode.text"), bundle.getString("offlineMode.title"), JOptionPane.WARNING_MESSAGE); break; case INCOMPATIBLE: break; } } private void modSelection(Mod m) { lblSubmitterVal.setText(""); lblCreatorVal.setText(""); lblDescriptionVal.setText(""); lblTypesVal.setText(""); lblPlatformVal.setText(""); lblPackageNumberVal.setText(""); if (m != null) { lblSubmitterVal.setText(m.getName()); lblCreatorVal.setText(m.getCreator()); lblDescriptionVal.setText(m.getDescription()); String types = ""; for (Type t : m.getTypes()) { if (types.length() > 0) types += ", "; types += t.getName(); } lblTypesVal.setText(types); lblPlatformVal.setText(m.getPlatform().toString()); lblPackageNumberVal.setText(m.getPackageNumberString()); } } @SuppressWarnings("unused") private void modTypeSelection() { Type t = (Type) cmbModTypes.getSelectedItem(); if (t != null) sorter.setRowFilter(new ModTableFilter(t)); else sorter.setRowFilter(new ModTableFilter(null)); } @Override public void downloadSizeChanged(int newSize) { lblDownloadSizeVal.setText(SizeFormatter.format(newSize, 2)); } @SuppressWarnings("unused") private void checkInitialize() { if (!Installer.isEditionInitialized()) { if (!OniSplit.isOniSplitInstalled()) { JOptionPane.showMessageDialog(this, bundle.getString("noOniSplit.text"), bundle.getString("noOniSplit.title"), JOptionPane.ERROR_MESSAGE); exit(); } else { int res = JOptionPane .showConfirmDialog(this, bundle.getString("askInitialize.text"), bundle.getString("askInitialize.title"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (res == JOptionPane.NO_OPTION) { saveLocalData(); exit(); } } } } @DoInBackground(progressMessage = "initializingEdition.title", cancelable = false, indeterminateProgress = false) private void initialize(final BackgroundEvent evt) { if (!Installer.isEditionInitialized()) { Installer.initializeEdition(new InstallProgressListener() { @Override public void installProgressUpdate(int done, int total, String step) { evt.setProgressEnd(total); evt.setProgressValue(done); evt.setProgressMessage(step); } }); } } private Vector getBasicOniLaunchParams() { Vector params = new Vector(); File exe = null; switch (Settings.getPlatform()) { case WIN: exe = new File(Paths.getEditionBasePath(), "Oni.exe"); if (exe.exists()) params.add(exe.getPath()); break; case MACOS: exe = new File(Paths.getEditionBasePath(), "Oni.app/Contents/MacOS/Oni"); if (exe.exists()) params.add(exe.getPath()); break; case LINUX: String wine = Settings.getWinePath(); exe = new File(Paths.getEditionBasePath(), "Oni.exe"); if (exe.exists()) { if (wine != null) { params.add(wine); params.add(exe.getPath()); } } break; default: } if (params.size() > 0) { params.add("-debugfiles"); } return params; } @SuppressWarnings("unused") private void oniFull() { Vector params = getBasicOniLaunchParams(); if (params.size() > 0) { AppExecution.execute(params, Paths.getEditionBasePath()); } } @SuppressWarnings("unused") private void oniWin() { Vector params = getBasicOniLaunchParams(); if (params.size() > 0) { params.add("-noswitch"); AppExecution.execute(params, Paths.getEditionBasePath()); } } @SuppressWarnings("unused") private void openEditionFolder() { try { Desktop.getDesktop().open(Paths.getEditionBasePath()); } catch (IOException e) { e.printStackTrace(); } } @Override public void handleAbout(ApplicationEvent event) { event.setHandled(true); showAbout(); } @Override public void handleOpenApplication(ApplicationEvent event) { } @Override public void handleOpenFile(ApplicationEvent event) { } @Override public void handlePreferences(ApplicationEvent event) { showSettings(); } @Override public void handlePrintFile(ApplicationEvent event) { } @Override public void handleQuit(ApplicationEvent event) { event.setHandled(true); saveLocalData(); exit(); } @Override public void handleReOpenApplication(ApplicationEvent event) { } }