source: AE/installer2/src/net/oni2/aeinstaller/gui/MainWin.java@ 631

Last change on this file since 631 was 631, checked in by alloc, 12 years ago

AEI2 0.90:

  • Added blank persist.dat for UnlockLevel to always work
  • Made local paths use the sanitized mod-name
  • Added mod-state column to mod table
File size: 20.6 KB
Line 
1package net.oni2.aeinstaller.gui;
2
3import java.awt.BorderLayout;
4import java.awt.Desktop;
5import java.awt.event.ActionEvent;
6import java.awt.event.ItemEvent;
7import java.awt.event.ItemListener;
8import java.io.File;
9import java.io.IOException;
10import java.net.URL;
11import java.util.HashMap;
12import java.util.HashSet;
13import java.util.ResourceBundle;
14import java.util.TreeMap;
15import java.util.TreeSet;
16import java.util.Vector;
17
18import javax.swing.AbstractAction;
19import javax.swing.Icon;
20import javax.swing.ImageIcon;
21import javax.swing.JButton;
22import javax.swing.JCheckBox;
23import javax.swing.JComboBox;
24import javax.swing.JFileChooser;
25import javax.swing.JFrame;
26import javax.swing.JLabel;
27import javax.swing.JMenu;
28import javax.swing.JMenuItem;
29import javax.swing.JOptionPane;
30import javax.swing.JPanel;
31import javax.swing.JRadioButton;
32import javax.swing.JSplitPane;
33import javax.swing.SwingUtilities;
34import javax.swing.ToolTipManager;
35import javax.swing.filechooser.FileFilter;
36
37import net.oni2.aeinstaller.AEInstaller2;
38import net.oni2.aeinstaller.backend.AppExecution;
39import net.oni2.aeinstaller.backend.Paths;
40import net.oni2.aeinstaller.backend.Settings;
41import net.oni2.aeinstaller.backend.Settings.Platform;
42import net.oni2.aeinstaller.backend.SizeFormatter;
43import net.oni2.aeinstaller.backend.depot.DepotCacheUpdateProgressListener;
44import net.oni2.aeinstaller.backend.depot.DepotManager;
45import net.oni2.aeinstaller.backend.mods.Mod;
46import net.oni2.aeinstaller.backend.mods.ModManager;
47import net.oni2.aeinstaller.backend.mods.Type;
48import net.oni2.aeinstaller.backend.mods.download.ModDownloader;
49import net.oni2.aeinstaller.backend.mods.download.ModDownloader.State;
50import net.oni2.aeinstaller.backend.mods.download.ModDownloaderListener;
51import net.oni2.aeinstaller.backend.oni.InstallProgressListener;
52import net.oni2.aeinstaller.backend.oni.Installer;
53import net.oni2.aeinstaller.backend.oni.OniSplit;
54import net.oni2.aeinstaller.gui.about.AboutDialog;
55import net.oni2.aeinstaller.gui.downloadwindow.Downloader;
56import net.oni2.aeinstaller.gui.modtable.DownloadSizeListener;
57import net.oni2.aeinstaller.gui.modtable.ModSelectionListener;
58import net.oni2.aeinstaller.gui.modtable.ModTable;
59import net.oni2.aeinstaller.gui.settings.SettingsDialog;
60import net.oni2.aeinstaller.gui.toolmanager.ToolManager;
61
62import org.javabuilders.BuildResult;
63import org.javabuilders.annotations.DoInBackground;
64import org.javabuilders.event.BackgroundEvent;
65import org.javabuilders.swing.SwingJavaBuilder;
66import org.simplericity.macify.eawt.ApplicationEvent;
67import org.simplericity.macify.eawt.ApplicationListener;
68
69/**
70 * @author Christian Illy
71 */
72public class MainWin extends JFrame implements ApplicationListener,
73 DownloadSizeListener, ModSelectionListener {
74 private static final long serialVersionUID = -4027395051382659650L;
75
76 private ResourceBundle bundle = ResourceBundle
77 .getBundle("net.oni2.aeinstaller.localization."
78 + getClass().getSimpleName());
79 @SuppressWarnings("unused")
80 private BuildResult result = SwingJavaBuilder.build(this, bundle);
81
82 private JMenu mainMenu;
83 private JMenu toolsMenu;
84 private TreeSet<JMenuItem> toolsMenuItems = new TreeSet<JMenuItem>();
85
86 private JSplitPane contents;
87
88 private JComboBox cmbModTypes;
89 private JRadioButton radAll;
90 private JRadioButton radOnline;
91 private JRadioButton radLocal;
92 private ModTable tblMods;
93 private JLabel lblDownloadSizeVal;
94
95 private JLabel lblSubmitterVal;
96 private JLabel lblCreatorVal;
97 private JLabel lblTypesVal;
98 private JLabel lblPlatformVal;
99 private JLabel lblPackageNumberVal;
100 private HTMLLinkLabel lblDescriptionVal;
101
102 private JButton btnInstall;
103
104 private TreeSet<Mod> execUpdates = null;
105
106 private enum EInstallResult {
107 DONE,
108 OFFLINE,
109 INCOMPATIBLE
110 };
111
112 private EInstallResult installDone = EInstallResult.DONE;
113
114 /**
115 * Constructor of main window.
116 */
117 public MainWin() {
118 this.setTitle(SwingJavaBuilder.getConfig().getResource("appname")
119 + " - v"
120 + SwingJavaBuilder.getConfig().getResource("appversion"));
121
122 contents.setDividerLocation(400);
123 contents.setResizeWeight(0.4);
124
125 if (Settings.getPlatform() == Platform.MACOS) {
126 mainMenu.setVisible(false);
127 }
128
129 ToolTipManager.sharedInstance().setInitialDelay(250);
130
131 getRootPane().setDefaultButton(btnInstall);
132 lblDownloadSizeVal.setText(SizeFormatter.format(0, 2));
133 radAll.setSelected(true);
134
135 tblMods.addModSelectionListener(this);
136 tblMods.addDownloadSizeListener(this);
137 }
138
139 private void initModTypeBox() {
140 cmbModTypes.removeAllItems();
141
142 TreeMap<String, Type> types = new TreeMap<String, Type>();
143 for (Type t : ModManager.getInstance().getTypesWithContent()) {
144 types.put(t.getName(), t);
145 }
146 cmbModTypes.addItem("-All-");
147 for (Type t : types.values()) {
148 cmbModTypes.addItem(t);
149 }
150 cmbModTypes.setSelectedIndex(0);
151 }
152
153 private void exit() {
154 dispose();
155 System.exit(0);
156 }
157
158 private void saveLocalData() {
159 Settings.getInstance().serializeToFile();
160 DepotManager.getInstance().saveToFile(Settings.getDepotCacheFilename());
161 }
162
163 @DoInBackground(progressMessage = "updateDepot.title", cancelable = false, indeterminateProgress = false)
164 private void execDepotUpdate(final BackgroundEvent evt) {
165 if (!Settings.getInstance().isOfflineMode()) {
166 try {
167 DepotManager.getInstance().updateInformation(false,
168 new DepotCacheUpdateProgressListener() {
169
170 @Override
171 public void cacheUpdateProgress(String stepName,
172 int current, int total) {
173 evt.setProgressEnd(total);
174 evt.setProgressValue(current);
175 evt.setProgressMessage(stepName);
176 }
177 });
178 } catch (Exception e) {
179 e.printStackTrace();
180 }
181 }
182 ModManager.getInstance().init();
183 tblMods.reloadData();
184 initModTypeBox();
185
186 tblMods.setVisible(true);
187 }
188
189 @SuppressWarnings("unused")
190 private void checkUpdates(Object evtSource) {
191 if ((evtSource != this)
192 || Settings.getInstance().get("notifyupdates", true)) {
193 if (Settings.getInstance().isOfflineMode()) {
194 if (evtSource != this) {
195 JOptionPane.showMessageDialog(this,
196 bundle.getString("offlineMode.text"),
197 bundle.getString("offlineMode.title"),
198 JOptionPane.WARNING_MESSAGE);
199 }
200 } else {
201 TreeSet<Mod> mods = ModManager.getInstance().getUpdatableMods();
202 TreeSet<Mod> tools = ModManager.getInstance()
203 .getUpdatableTools();
204 int size = 0;
205 String strMods = "";
206 for (Mod m : mods) {
207 size += m.getZipSize();
208 if (strMods.length() > 0)
209 strMods += "<br>";
210 strMods += " - " + m.getName();
211 }
212 String strTools = "";
213 for (Mod m : tools) {
214 size += m.getZipSize();
215 if (strTools.length() > 0)
216 strTools += "<br>";
217 strTools += " - " + m.getName();
218 }
219 if (size > 0) {
220 // Build info dialog content
221 String message = "<html>";
222 message += String.format(
223 bundle.getString("updatesAvailable.text"), strMods,
224 strTools, SizeFormatter.format(size, 3));
225 message += "</html>";
226
227 JPanel pan = new JPanel();
228 pan.setLayout(new BorderLayout(0, 20));
229 JLabel lab = new JLabel(message);
230 pan.add(lab, BorderLayout.CENTER);
231 JCheckBox checkFutureUpdates = new JCheckBox(
232 bundle.getString("checkOnStartup.text"));
233 checkFutureUpdates.setSelected(Settings.getInstance().get(
234 "notifyupdates", true));
235 checkFutureUpdates.addItemListener(new ItemListener() {
236 @Override
237 public void itemStateChanged(ItemEvent evt) {
238 Settings.getInstance().put("notifyupdates",
239 evt.getStateChange() == ItemEvent.SELECTED);
240 }
241 });
242 pan.add(checkFutureUpdates, BorderLayout.SOUTH);
243
244 // Show dialog
245 int res = JOptionPane.showConfirmDialog(this, pan,
246 bundle.getString("updatesAvailable.title"),
247 JOptionPane.YES_NO_OPTION,
248 JOptionPane.QUESTION_MESSAGE);
249 if (res == JOptionPane.YES_OPTION) {
250 execUpdates = new TreeSet<Mod>();
251 execUpdates.addAll(mods);
252 execUpdates.addAll(tools);
253 }
254 }
255 }
256 }
257 }
258
259 @SuppressWarnings("unused")
260 private void doUpdate() {
261 if (execUpdates != null) {
262 Downloader dl = new Downloader(execUpdates);
263 try {
264 dl.setVisible(true);
265 if (dl.isFinished()) {
266 TreeSet<Integer> installed = Installer.getInstalledTools();
267 TreeSet<Mod> tools = new TreeSet<Mod>();
268 for (Mod m : execUpdates)
269 if (m.isTool()
270 && installed.contains(m.getPackageNumber()))
271 tools.add(m);
272 if (tools.size() > 0) {
273 Installer.installTools(tools);
274 }
275 }
276 } finally {
277 dl.dispose();
278 }
279 }
280 execUpdates = null;
281 }
282
283 @SuppressWarnings("unused")
284 private void focus() {
285 SwingUtilities.invokeLater(new Runnable() {
286
287 @Override
288 public void run() {
289 toFront();
290 repaint();
291 }
292 });
293
294 }
295
296 private void showSettings() {
297 new SettingsDialog().setVisible(true);
298 }
299
300 private void showAbout() {
301 new AboutDialog().setVisible(true);
302 }
303
304 private JFileChooser getConfigOpenSaveDialog(boolean save) {
305 JFileChooser fc = new JFileChooser();
306 fc.setCurrentDirectory(Paths.getEditionBasePath());
307 if (save)
308 fc.setDialogType(JFileChooser.SAVE_DIALOG);
309 else
310 fc.setDialogType(JFileChooser.OPEN_DIALOG);
311 fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
312 fc.setFileFilter(new FileFilter() {
313 @Override
314 public String getDescription() {
315 return "XML files";
316 }
317
318 @Override
319 public boolean accept(File arg0) {
320 return (arg0.isDirectory())
321 || (arg0.getName().toLowerCase().endsWith(".xml"));
322 }
323 });
324 fc.setMultiSelectionEnabled(false);
325 return fc;
326 }
327
328 @SuppressWarnings("unused")
329 private void loadConfig() {
330 JFileChooser fc = getConfigOpenSaveDialog(false);
331 int res = fc.showOpenDialog(this);
332 if (res == JFileChooser.APPROVE_OPTION) {
333 if (fc.getSelectedFile().exists())
334 tblMods.reloadSelection(fc.getSelectedFile());
335 }
336 }
337
338 @SuppressWarnings("unused")
339 private void saveConfig() {
340 JFileChooser fc = getConfigOpenSaveDialog(true);
341 int res = fc.showSaveDialog(this);
342 if (res == JFileChooser.APPROVE_OPTION) {
343 File f = fc.getSelectedFile();
344 if (!f.getName().endsWith(".xml"))
345 f = new File(f.getParentFile(), f.getName() + ".xml");
346 ModManager.getInstance().saveModSelection(f,
347 tblMods.getSelectedMods());
348 }
349 }
350
351 @DoInBackground(progressMessage = "initializingEdition.title", cancelable = false, indeterminateProgress = false)
352 private void reglobalize(final BackgroundEvent evt) {
353 Installer.initializeEdition(new InstallProgressListener() {
354 @Override
355 public void installProgressUpdate(int done, int total, String step) {
356 evt.setProgressEnd(total);
357 evt.setProgressValue(done);
358 evt.setProgressMessage(step);
359 }
360 });
361 }
362
363 @SuppressWarnings("unused")
364 private void tools() {
365 new ToolManager().setVisible(true);
366 }
367
368 @SuppressWarnings("unused")
369 private void refreshToolsMenu() {
370 for (JMenuItem i : toolsMenuItems) {
371 toolsMenu.remove(i);
372 }
373 toolsMenuItems.clear();
374 for (Mod m : ModManager.getInstance().getInstalledTools()) {
375 if (m.getExeFile() != null && m.getExeFile().exists()) {
376 JMenuItem item = new JMenuItem();
377 final Vector<String> params = new Vector<String>();
378 params.add(m.getExeFile().getPath());
379 final File wd = m.getWorkingDir();
380 Icon ico = null;
381 if (m.getIconFile() != null && m.getIconFile().exists()) {
382 ico = new ImageIcon(m.getIconFile().getPath());
383 } else {
384 URL icon = AEInstaller2.class
385 .getResource("images/transparent.png");
386 ico = new ImageIcon(icon);
387 }
388 item.setAction(new AbstractAction(m.getName(), ico) {
389 private static final long serialVersionUID = 1L;
390
391 @Override
392 public void actionPerformed(ActionEvent e) {
393 AppExecution.execute(params, wd);
394 }
395 });
396 toolsMenuItems.add(item);
397 toolsMenu.add(item);
398 }
399 }
400 }
401
402 @SuppressWarnings("unused")
403 private void revertSelection() {
404 tblMods.revertSelection();
405 }
406
407 @DoInBackground(progressMessage = "mandatoryFiles.title", cancelable = false, indeterminateProgress = false)
408 private void checkMandatoryFiles(final BackgroundEvent evt) {
409 if (!Settings.getInstance().isOfflineMode()) {
410 TreeSet<Mod> mand = new TreeSet<Mod>();
411 for (Mod m : ModManager.getInstance().getMandatoryTools()) {
412 if (m.isNewerAvailable()) {
413 mand.add(m);
414 }
415 }
416 for (Mod m : ModManager.getInstance().getMandatoryMods()) {
417 if (m.isNewerAvailable()) {
418 mand.add(m);
419 }
420 }
421 if (mand.size() > 0) {
422 ModDownloader m = new ModDownloader(mand,
423 new ModDownloaderListener() {
424 @Override
425 public void updateStatus(ModDownloader source,
426 State state, int filesDown, int filesTotal,
427 int bytesDown, int bytesTotal,
428 int duration, int remaining, int speed) {
429 evt.setProgressEnd(filesTotal);
430 evt.setProgressValue(filesDown);
431 }
432 });
433 while (!m.isFinished()) {
434 try {
435 Thread.sleep(10);
436 } catch (InterruptedException e) {
437 e.printStackTrace();
438 }
439 }
440 }
441 evt.setProgressMessage(bundle
442 .getString("mandatoryToolsInstall.title"));
443 Installer
444 .installTools(ModManager.getInstance().getMandatoryTools());
445 }
446 }
447
448 @DoInBackground(progressMessage = "installing.title", cancelable = false, indeterminateProgress = false)
449 private void install(final BackgroundEvent evt) {
450 TreeSet<Mod> mods = new TreeSet<Mod>();
451 mods.addAll(ModManager.getInstance().getMandatoryMods());
452 mods.addAll(tblMods.getSelectedMods());
453
454 boolean instReady = false;
455 installDone = EInstallResult.DONE;
456
457 while (!instReady) {
458 TreeSet<Mod> toDownload = new TreeSet<Mod>();
459 for (Mod m : mods) {
460 if (!m.isLocalAvailable())
461 toDownload.add(m);
462 }
463 if (Settings.getInstance().isOfflineMode()) {
464 installDone = EInstallResult.OFFLINE;
465 break;
466 }
467 if (toDownload.size() > 0) {
468 Downloader dl = new Downloader(toDownload);
469 try {
470 dl.setVisible(true);
471 if (!dl.isFinished())
472 break;
473 } finally {
474 dl.dispose();
475 }
476 }
477 HashMap<Mod, HashSet<Mod>> dependencies = ModManager.getInstance()
478 .checkDependencies(mods);
479 if (dependencies.size() > 0) {
480 System.out.println("Unmet dependencies: "
481 + dependencies.toString());
482 for (Mod m : dependencies.keySet()) {
483 for (Mod mDep : dependencies.get(m))
484 mods.add(mDep);
485 }
486 } else {
487 HashMap<Mod, HashSet<Mod>> conflicts = ModManager.getInstance()
488 .checkIncompabitilites(mods);
489 if (conflicts.size() > 0) {
490 installDone = EInstallResult.INCOMPATIBLE;
491 System.err.println("Incompatible mods: "
492 + conflicts.toString());
493 break;
494 } else {
495 instReady = true;
496 }
497 }
498 }
499
500 if (instReady) {
501 TreeSet<Mod> actuallyMods = new TreeSet<Mod>();
502 TreeSet<Mod> actuallyTools = new TreeSet<Mod>();
503
504 for (Mod m : mods) {
505 if (m.isTool())
506 actuallyTools.add(m);
507 else
508 actuallyMods.add(m);
509 }
510
511 if (actuallyTools.size() > 0) {
512 Installer.installTools(actuallyTools);
513 }
514
515 Installer.install(actuallyMods, new InstallProgressListener() {
516 @Override
517 public void installProgressUpdate(int done, int total,
518 String step) {
519 evt.setProgressEnd(total);
520 evt.setProgressValue(done);
521 evt.setProgressMessage(step);
522 }
523 });
524 installDone = EInstallResult.DONE;
525 }
526 }
527
528 @SuppressWarnings("unused")
529 private void installDone() {
530 switch (installDone) {
531 case DONE:
532 JOptionPane.showMessageDialog(this,
533 bundle.getString("installDone.text"),
534 bundle.getString("installDone.title"),
535 JOptionPane.INFORMATION_MESSAGE);
536 break;
537 case OFFLINE:
538 JOptionPane.showMessageDialog(this,
539 bundle.getString("offlineMode.text"),
540 bundle.getString("offlineMode.title"),
541 JOptionPane.WARNING_MESSAGE);
542 break;
543 case INCOMPATIBLE:
544 break;
545 }
546 }
547
548 @Override
549 public void modSelectionChanged(ModTable source, Mod m) {
550 lblSubmitterVal.setText("");
551 lblCreatorVal.setText("");
552 lblDescriptionVal.setText("");
553 lblTypesVal.setText("");
554 lblPlatformVal.setText("");
555 lblPackageNumberVal.setText("");
556 if (m != null) {
557 lblSubmitterVal.setText(m.getName());
558 lblCreatorVal.setText(m.getCreator());
559 lblDescriptionVal.setText(m.getDescription());
560
561 String types = "";
562 for (Type t : m.getTypes()) {
563 if (types.length() > 0)
564 types += ", ";
565 types += t.getName();
566 }
567 lblTypesVal.setText(types);
568 lblPlatformVal.setText(m.getPlatform().toString());
569 lblPackageNumberVal.setText(m.getPackageNumberString());
570 }
571 }
572
573 private void updateTableFilter() {
574 Object o = cmbModTypes.getSelectedItem();
575 Type t = null;
576 if (o instanceof Type)
577 t = (Type) o;
578 int downloadState = 0;
579 if (radOnline.isSelected())
580 downloadState = 1;
581 if (radLocal.isSelected())
582 downloadState = 2;
583 tblMods.setFilter(t, downloadState);
584 }
585
586 @SuppressWarnings("unused")
587 private void modTypeSelection() {
588 updateTableFilter();
589 }
590
591 @SuppressWarnings("unused")
592 private void showTypeSelection() {
593 updateTableFilter();
594 }
595
596 @Override
597 public void downloadSizeChanged(int newSize) {
598 lblDownloadSizeVal.setText(SizeFormatter.format(newSize, 2));
599 }
600
601 @SuppressWarnings("unused")
602 private void checkInitialize() {
603 if (!Installer.isEditionInitialized()) {
604 if (!OniSplit.isOniSplitInstalled()) {
605 JOptionPane.showMessageDialog(this,
606 bundle.getString("noOniSplit.text"),
607 bundle.getString("noOniSplit.title"),
608 JOptionPane.ERROR_MESSAGE);
609 exit();
610 } else {
611 int res = JOptionPane
612 .showConfirmDialog(this,
613 bundle.getString("askInitialize.text"),
614 bundle.getString("askInitialize.title"),
615 JOptionPane.YES_NO_OPTION,
616 JOptionPane.QUESTION_MESSAGE);
617 if (res == JOptionPane.NO_OPTION) {
618 saveLocalData();
619 exit();
620 }
621 }
622 }
623 }
624
625 @DoInBackground(progressMessage = "initializingEdition.title", cancelable = false, indeterminateProgress = false)
626 private void initialize(final BackgroundEvent evt) {
627 if (!Installer.isEditionInitialized()) {
628 Installer.initializeEdition(new InstallProgressListener() {
629 @Override
630 public void installProgressUpdate(int done, int total,
631 String step) {
632 evt.setProgressEnd(total);
633 evt.setProgressValue(done);
634 evt.setProgressMessage(step);
635 }
636 });
637 }
638 }
639
640 private Vector<String> getBasicOniLaunchParams() {
641 Vector<String> params = new Vector<String>();
642 File exe = null;
643 switch (Settings.getPlatform()) {
644 case WIN:
645 exe = new File(Paths.getEditionBasePath(), "Oni.exe");
646 if (exe.exists())
647 params.add(exe.getPath());
648 break;
649 case MACOS:
650 exe = new File(Paths.getEditionBasePath(),
651 "Oni.app/Contents/MacOS/Oni");
652 if (exe.exists())
653 params.add(exe.getPath());
654 break;
655 case LINUX:
656 String wine = Settings.getWinePath();
657 exe = new File(Paths.getEditionBasePath(), "Oni.exe");
658 if (exe.exists()) {
659 if (wine != null) {
660 params.add(wine);
661 params.add(exe.getPath());
662 }
663 }
664 break;
665 default:
666 }
667 if (params.size() > 0) {
668 params.add("-debugfiles");
669 }
670 return params;
671 }
672
673 @SuppressWarnings("unused")
674 private void oniFull() {
675 Vector<String> params = getBasicOniLaunchParams();
676 if (params.size() > 0) {
677 AppExecution.execute(params, Paths.getEditionBasePath());
678 }
679 }
680
681 @SuppressWarnings("unused")
682 private void oniWin() {
683 Vector<String> params = getBasicOniLaunchParams();
684 if (params.size() > 0) {
685 params.add("-noswitch");
686 AppExecution.execute(params, Paths.getEditionBasePath());
687 }
688 }
689
690 @SuppressWarnings("unused")
691 private void openEditionFolder() {
692 try {
693 Desktop.getDesktop().open(Paths.getEditionBasePath());
694 } catch (IOException e) {
695 e.printStackTrace();
696 }
697 }
698
699 @Override
700 public void handleAbout(ApplicationEvent event) {
701 event.setHandled(true);
702 showAbout();
703 }
704
705 @Override
706 public void handleOpenApplication(ApplicationEvent event) {
707 }
708
709 @Override
710 public void handleOpenFile(ApplicationEvent event) {
711 }
712
713 @Override
714 public void handlePreferences(ApplicationEvent event) {
715 showSettings();
716 }
717
718 @Override
719 public void handlePrintFile(ApplicationEvent event) {
720 }
721
722 @Override
723 public void handleQuit(ApplicationEvent event) {
724 event.setHandled(true);
725 saveLocalData();
726 exit();
727 }
728
729 @Override
730 public void handleReOpenApplication(ApplicationEvent event) {
731 }
732
733}
Note: See TracBrowser for help on using the repository browser.