001: /*
002: * SwingInstall.java
003: *
004: * Originally written by Slava Pestov for the jEdit installer project. This work
005: * has been placed into the public domain. You may use this work in any way and
006: * for any purpose you wish.
007: *
008: * THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, NOT EVEN THE
009: * IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, ASSUMES
010: * _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE RESULTING FROM THE USE, MODIFICATION,
011: * OR REDISTRIBUTION OF THIS SOFTWARE.
012: */
013:
014: package installer;
015:
016: import javax.swing.border.*;
017: import javax.swing.*;
018: import java.awt.event.*;
019: import java.awt.*;
020: import java.io.File;
021: import java.util.*;
022:
023: /*
024: * Graphical front-end to installer.
025: */
026: public class SwingInstall extends JFrame {
027: public SwingInstall() {
028: installer = new Install();
029: osTasks = OperatingSystem.getOperatingSystem().getOSTasks(
030: installer);
031:
032: appName = installer.getProperty("app.name");
033: appVersion = installer.getProperty("app.version");
034:
035: setTitle(appName + " " + appVersion + " installer");
036:
037: JPanel content = new JPanel(new WizardLayout());
038: setContentPane(content);
039:
040: caption = new JLabel();
041: caption.setFont(new Font("SansSerif", Font.BOLD, 18));
042:
043: ActionHandler actionHandler = new ActionHandler();
044:
045: cancelButton = new JButton("Cancel");
046: cancelButton.setRequestFocusEnabled(false);
047: cancelButton.addActionListener(actionHandler);
048: prevButton = new JButton("Previous");
049: prevButton.setRequestFocusEnabled(false);
050: prevButton.addActionListener(actionHandler);
051: nextButton = new JButton();
052: nextButton.setRequestFocusEnabled(false);
053: nextButton.addActionListener(actionHandler);
054:
055: content.add(caption);
056: content.add(cancelButton);
057: content.add(prevButton);
058: content.add(nextButton);
059:
060: String clazz = OperatingSystem.getOperatingSystem().getClass()
061: .getName();
062: String completedInfo = "done-"
063: + clazz.substring(clazz.indexOf('$') + 1) + ".html";
064:
065: pages = new Component[] {
066: new TextPanel(installer.getProperty("app.readme")),
067: new TextPanel(installer.getProperty("app.license")),
068: chooseDirectory = new ChooseDirectory(),
069: selectComponents = new SelectComponents(),
070: progress = new SwingProgress(),
071: new TextPanel(completedInfo) };
072:
073: for (int i = 0; i < pages.length; i++)
074: content.add(pages[i]);
075:
076: pageChanged();
077:
078: setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
079: addWindowListener(new WindowHandler());
080:
081: Dimension screen = getToolkit().getScreenSize();
082: pack();
083: setLocation((screen.width - getSize().width) / 2,
084: (screen.height - getSize().height) / 2);
085: setVisible(true);
086: }
087:
088: // package-private members
089: // package-private, not private, for speedy access by inner classes
090: Install installer;
091: OperatingSystem.OSTask[] osTasks;
092: String appName;
093: String appVersion;
094:
095: JLabel caption;
096:
097: ChooseDirectory chooseDirectory;
098: SelectComponents selectComponents;
099: SwingProgress progress;
100:
101: JButton cancelButton;
102: JButton prevButton;
103: JButton nextButton;
104: Component[] pages;
105: int currentPage;
106:
107: private static final int PADDING = 12;
108:
109: void install() {
110: Vector components = new Vector();
111: int size = 0;
112:
113: JPanel comp = selectComponents.comp;
114: Vector ids = selectComponents.filesets;
115:
116: for (int i = 0; i < comp.getComponentCount(); i++) {
117: if (((JCheckBox) comp.getComponent(i)).getModel()
118: .isSelected()) {
119: size += installer.getIntegerProperty("comp."
120: + ids.elementAt(i) + ".real-size");
121: components.addElement(installer.getProperty("comp."
122: + ids.elementAt(i) + ".fileset"));
123: }
124: }
125:
126: String installDir = chooseDirectory.installDir.getText();
127:
128: Map osTaskDirs = chooseDirectory.osTaskDirs;
129: Iterator keys = osTaskDirs.keySet().iterator();
130: while (keys.hasNext()) {
131: OperatingSystem.OSTask osTask = (OperatingSystem.OSTask) keys
132: .next();
133: String dir = ((JTextField) osTaskDirs.get(osTask))
134: .getText();
135: if (dir != null && dir.length() != 0) {
136: osTask.setEnabled(true);
137: osTask.setDirectory(dir);
138: } else
139: osTask.setEnabled(false);
140: }
141:
142: InstallThread thread = new InstallThread(installer, progress,
143: installDir, osTasks, size, components);
144: progress.setThread(thread);
145: thread.start();
146: }
147:
148: private void pageChanged() {
149: switch (currentPage) {
150: case 0:
151: caption.setText("Installing " + appName);
152:
153: nextButton.setText("Next");
154: prevButton.setEnabled(false);
155: break;
156: case 1:
157: caption.setText(installer.getProperty("app.license.title"));
158:
159: nextButton.setText("Next");
160: prevButton.setEnabled(true);
161: break;
162: case 2:
163: caption.setText("Specify where " + appName
164: + " is to be installed");
165:
166: nextButton.setText("Next");
167: prevButton.setEnabled(true);
168: break;
169: case 3:
170: caption.setText("Choose components to install");
171:
172: nextButton.setText("Install");
173: prevButton.setEnabled(true);
174: break;
175: case 4:
176: caption.setText("Installing " + appName);
177:
178: nextButton.setText("Finish");
179: prevButton.setEnabled(false);
180: nextButton.setEnabled(false);
181: install();
182: break;
183: case 5:
184: caption.setText("Installation complete");
185:
186: nextButton.setText("Finish");
187: prevButton.setEnabled(false);
188: nextButton.setEnabled(true);
189: break;
190: }
191:
192: getRootPane().invalidate();
193: getRootPane().validate();
194: }
195:
196: class ActionHandler implements ActionListener {
197: public void actionPerformed(ActionEvent evt) {
198: Object source = evt.getSource();
199: if (source == cancelButton)
200: System.exit(0);
201: else if (source == prevButton) {
202: currentPage--;
203: pageChanged();
204: } else if (source == nextButton) {
205: if (currentPage == pages.length - 1)
206: System.exit(0);
207: else {
208: currentPage++;
209: pageChanged();
210: }
211: }
212: }
213: }
214:
215: class WindowHandler extends WindowAdapter {
216: public void windowClosing(WindowEvent evt) {
217: System.exit(0);
218: }
219: }
220:
221: class WizardLayout implements LayoutManager {
222: public void addLayoutComponent(String name, Component comp) {
223: }
224:
225: public void removeLayoutComponent(Component comp) {
226: }
227:
228: public Dimension preferredLayoutSize(Container parent) {
229: Dimension dim = new Dimension();
230:
231: Dimension captionSize = caption.getPreferredSize();
232: dim.width = captionSize.width;
233:
234: for (int i = 0; i < pages.length; i++) {
235: Dimension _dim = pages[i].getPreferredSize();
236: dim.width = Math.max(_dim.width, dim.width);
237: dim.height = Math.max(_dim.height, dim.height);
238: }
239:
240: dim.width += PADDING * 2;
241: dim.height += PADDING * 2;
242: dim.height += nextButton.getPreferredSize().height;
243: dim.height += captionSize.height;
244: return dim;
245: }
246:
247: public Dimension minimumLayoutSize(Container parent) {
248: return preferredLayoutSize(parent);
249: }
250:
251: public void layoutContainer(Container parent) {
252: Dimension size = parent.getSize();
253:
254: Dimension captionSize = caption.getPreferredSize();
255: caption.setBounds(PADDING, PADDING, captionSize.width,
256: captionSize.height);
257:
258: // make all buttons the same size
259: Dimension buttonSize = cancelButton.getPreferredSize();
260: buttonSize.width = Math.max(buttonSize.width, prevButton
261: .getPreferredSize().width);
262: buttonSize.width = Math.max(buttonSize.width, nextButton
263: .getPreferredSize().width);
264:
265: // cancel button goes on far left
266: cancelButton.setBounds(PADDING, size.height
267: - buttonSize.height - PADDING, buttonSize.width,
268: buttonSize.height);
269:
270: // prev and next buttons are on the right
271: prevButton.setBounds(size.width - buttonSize.width * 2 - 6
272: - PADDING, size.height - buttonSize.height
273: - PADDING, buttonSize.width, buttonSize.height);
274:
275: nextButton.setBounds(size.width - buttonSize.width
276: - PADDING, size.height - buttonSize.height
277: - PADDING, buttonSize.width, buttonSize.height);
278:
279: // calculate size for current page
280: Rectangle currentPageBounds = new Rectangle();
281: currentPageBounds.x = PADDING;
282: currentPageBounds.y = PADDING * 2 + captionSize.height;
283: currentPageBounds.width = size.width - currentPageBounds.x
284: - PADDING;
285: currentPageBounds.height = size.height - buttonSize.height
286: - currentPageBounds.y - PADDING * 2;
287:
288: for (int i = 0; i < pages.length; i++) {
289: Component page = pages[i];
290: page.setBounds(currentPageBounds);
291: page.setVisible(i == currentPage);
292: }
293: }
294: }
295:
296: class TextPanel extends JPanel {
297: TextPanel(String file) {
298: super (new BorderLayout());
299:
300: JEditorPane text = new JEditorPane();
301:
302: try {
303: text.setPage(TextPanel.this .getClass()
304: .getResource(file));
305: } catch (Exception e) {
306: text.setText("Error loading '" + file + "'");
307: e.printStackTrace();
308: }
309:
310: text.setEditable(false);
311:
312: JScrollPane scrollPane = new JScrollPane(text);
313: Dimension dim = new Dimension();
314: dim.width = 450;
315: dim.height = 200;
316: scrollPane.setPreferredSize(dim);
317: TextPanel.this .add(BorderLayout.CENTER, scrollPane);
318: }
319: }
320:
321: class ChooseDirectory extends JPanel {
322: JTextField installDir;
323: Map osTaskDirs;
324:
325: ChooseDirectory() {
326: super (new BorderLayout());
327:
328: osTaskDirs = new HashMap();
329:
330: JPanel directoryPanel = new JPanel(new VariableGridLayout(
331: VariableGridLayout.FIXED_NUM_COLUMNS, 3, 12, 12));
332:
333: installDir = addField(directoryPanel,
334: "Install program in:", OperatingSystem
335: .getOperatingSystem().getInstallDirectory(
336: appName, appVersion));
337:
338: for (int i = 0; i < osTasks.length; i++) {
339: OperatingSystem.OSTask osTask = osTasks[i];
340: String label = osTask.getLabel();
341: if (label != null) {
342: JTextField field = addField(directoryPanel, label,
343: osTask.getDirectory());
344: osTaskDirs.put(osTask, field);
345: }
346: }
347: ChooseDirectory.this
348: .add(BorderLayout.NORTH, directoryPanel);
349: }
350:
351: private JTextField addField(JPanel directoryPanel,
352: String label, String defaultText) {
353: JTextField field = new JTextField(defaultText);
354:
355: directoryPanel.add(new JLabel(label, SwingConstants.RIGHT));
356:
357: Box fieldBox = new Box(BoxLayout.Y_AXIS);
358: fieldBox.add(Box.createGlue());
359: Dimension dim = field.getPreferredSize();
360: dim.width = Integer.MAX_VALUE;
361: field.setMaximumSize(dim);
362: fieldBox.add(field);
363: fieldBox.add(Box.createGlue());
364: directoryPanel.add(fieldBox);
365: JButton choose = new JButton("Choose...");
366: choose.setRequestFocusEnabled(false);
367: choose.addActionListener(new ActionHandler(field));
368: directoryPanel.add(choose);
369:
370: return field;
371: }
372:
373: class ActionHandler implements ActionListener {
374: JTextField field;
375:
376: ActionHandler(JTextField field) {
377: this .field = field;
378: }
379:
380: public void actionPerformed(ActionEvent evt) {
381: File directory = new File(field.getText());
382: JFileChooser chooser = new JFileChooser(directory
383: .getParent());
384: chooser
385: .setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
386: chooser.setSelectedFile(directory);
387:
388: if (chooser.showOpenDialog(SwingInstall.this ) == JFileChooser.APPROVE_OPTION)
389: field.setText(chooser.getSelectedFile().getPath());
390: }
391: }
392: }
393:
394: class SelectComponents extends JPanel implements ActionListener {
395: JPanel comp;
396: JLabel sizeLabel;
397: Vector filesets;
398:
399: SelectComponents() {
400: super (new BorderLayout());
401:
402: comp = createCompPanel();
403: SelectComponents.this .add(BorderLayout.NORTH, comp);
404:
405: sizeLabel = new JLabel("", SwingConstants.LEFT);
406: SelectComponents.this .add(BorderLayout.SOUTH, sizeLabel);
407:
408: updateSize();
409: }
410:
411: public void actionPerformed(ActionEvent evt) {
412: updateSize();
413: }
414:
415: private JPanel createCompPanel() {
416: filesets = new Vector();
417:
418: int count = installer.getIntegerProperty("comp.count");
419: JPanel panel = new JPanel(new GridLayout(count, 1));
420:
421: String osClass = OperatingSystem.getOperatingSystem()
422: .getClass().getName();
423: osClass = osClass.substring(osClass.indexOf('$') + 1);
424:
425: for (int i = 0; i < count; i++) {
426: String os = installer.getProperty("comp." + i + ".os");
427:
428: if (os != null && !osClass.equals(os))
429: continue;
430:
431: JCheckBox checkBox = new JCheckBox(installer
432: .getProperty("comp." + i + ".name")
433: + " ("
434: + installer.getProperty("comp." + i
435: + ".disk-size") + "Kb)");
436: checkBox.getModel().setSelected(true);
437: checkBox.addActionListener(this );
438: checkBox.setRequestFocusEnabled(false);
439:
440: filesets.addElement(new Integer(i));
441:
442: panel.add(checkBox);
443: }
444:
445: Dimension dim = panel.getPreferredSize();
446: dim.width = Integer.MAX_VALUE;
447: panel.setMaximumSize(dim);
448:
449: return panel;
450: }
451:
452: private void updateSize() {
453: int size = 0;
454:
455: for (int i = 0; i < filesets.size(); i++) {
456: if (((JCheckBox) comp.getComponent(i)).getModel()
457: .isSelected()) {
458: size += installer.getIntegerProperty("comp."
459: + filesets.elementAt(i) + ".disk-size");
460: }
461: }
462:
463: sizeLabel.setText("Estimated disk usage of selected"
464: + " components: " + size + "Kb");
465: }
466: }
467:
468: class SwingProgress extends JPanel implements Progress {
469: JProgressBar progress;
470: InstallThread thread;
471:
472: SwingProgress() {
473: super (new BorderLayout());
474:
475: progress = new JProgressBar();
476: progress.setStringPainted(true);
477:
478: SwingProgress.this .add(BorderLayout.NORTH, progress);
479: }
480:
481: public void setMaximum(final int max) {
482: SwingUtilities.invokeLater(new Runnable() {
483: public void run() {
484: progress.setMaximum(max);
485: }
486: });
487: }
488:
489: public void advance(final int value) {
490: try {
491: SwingUtilities.invokeAndWait(new Runnable() {
492: public void run() {
493: progress.setValue(progress.getValue() + value);
494: }
495: });
496: Thread.yield();
497: } catch (Exception e) {
498: }
499: }
500:
501: public void done() {
502: SwingUtilities.invokeLater(new Runnable() {
503: public void run() {
504: currentPage++;
505: pageChanged();
506: }
507: });
508: }
509:
510: public void error(final String message) {
511: SwingUtilities.invokeLater(new Runnable() {
512: public void run() {
513: dispose();
514: JOptionPane.showMessageDialog(null, message,
515: "Installation aborted",
516: JOptionPane.ERROR_MESSAGE);
517: System.exit(1);
518: }
519: });
520: }
521:
522: public void setThread(InstallThread thread) {
523: this.thread = thread;
524: }
525: }
526: }
|