PageRenderTime 25ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/installer/SwingInstall.java

#
Java | 784 lines | 641 code | 114 blank | 29 comment | 48 complexity | 52cade6a5080fb49c00023d5c9d4174a MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * SwingInstall.java
  3. *
  4. * Originally written by Slava Pestov for the jEdit installer project. This work
  5. * has been placed into the public domain. You may use this work in any way and
  6. * for any purpose you wish.
  7. *
  8. * THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, NOT EVEN THE
  9. * IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, ASSUMES
  10. * _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE RESULTING FROM THE USE, MODIFICATION,
  11. * OR REDISTRIBUTION OF THIS SOFTWARE.
  12. */
  13. package installer;
  14. import javax.swing.border.*;
  15. import javax.swing.*;
  16. import javax.swing.text.JTextComponent;
  17. import java.awt.event.*;
  18. import java.awt.*;
  19. import java.io.File;
  20. import java.io.IOException;
  21. import java.util.*;
  22. /*
  23. * Graphical front-end to installer.
  24. */
  25. public class SwingInstall extends JFrame
  26. {
  27. public SwingInstall()
  28. {
  29. installer = new Install();
  30. osTasks = OperatingSystem.getOperatingSystem().getOSTasks(installer);
  31. appName = installer.getProperty("app.name");
  32. appVersion = installer.getProperty("app.version");
  33. setTitle(appName + " " + appVersion + " installer");
  34. JPanel content = new JPanel(new WizardLayout());
  35. setContentPane(content);
  36. caption = new JLabel();
  37. caption.setFont(new Font("SansSerif",Font.BOLD,18));
  38. ActionHandler actionHandler = new ActionHandler();
  39. cancelButton = new JButton("Cancel");
  40. cancelButton.setRequestFocusEnabled(false);
  41. cancelButton.addActionListener(actionHandler);
  42. prevButton = new JButton("Previous");
  43. prevButton.setRequestFocusEnabled(false);
  44. prevButton.addActionListener(actionHandler);
  45. nextButton = new JButton();
  46. nextButton.setRequestFocusEnabled(false);
  47. nextButton.addActionListener(actionHandler);
  48. content.add(caption);
  49. content.add(cancelButton);
  50. content.add(prevButton);
  51. content.add(nextButton);
  52. String clazz = OperatingSystem.getOperatingSystem()
  53. .getClass().getName();
  54. String completedInfo = "done-" + clazz.substring(
  55. clazz.indexOf('$') + 1) + ".html";
  56. pages = new Component[] {
  57. new TextPanel(installer.getProperty("app.readme")),
  58. new TextPanel(installer.getProperty("app.license")),
  59. chooseDirectory = new ChooseDirectory(),
  60. selectComponents = new SelectComponents(),
  61. progress = new SwingProgress(),
  62. new TextPanel(completedInfo)
  63. };
  64. for(int i = 0; i < pages.length; i++)
  65. content.add(pages[i]);
  66. pageChanged();
  67. setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
  68. addWindowListener(new WindowHandler());
  69. Dimension screen = getToolkit().getScreenSize();
  70. pack();
  71. setLocation((screen.width - getSize().width) / 2,
  72. (screen.height - getSize().height) / 2);
  73. setVisible(true);
  74. }
  75. // package-private members
  76. // package-private, not private, for speedy access by inner classes
  77. Install installer;
  78. OperatingSystem.OSTask[] osTasks;
  79. String appName;
  80. String appVersion;
  81. JLabel caption;
  82. ChooseDirectory chooseDirectory;
  83. SelectComponents selectComponents;
  84. SwingProgress progress;
  85. JButton cancelButton;
  86. JButton prevButton;
  87. JButton nextButton;
  88. Component[] pages;
  89. int currentPage;
  90. private static final int PADDING = 12;
  91. void install()
  92. {
  93. Vector components = new Vector();
  94. int size = 0;
  95. JPanel comp = selectComponents.comp;
  96. Vector ids = selectComponents.filesets;
  97. for(int i = 0; i < comp.getComponentCount(); i++)
  98. {
  99. if(((JCheckBox)comp.getComponent(i))
  100. .getModel().isSelected())
  101. {
  102. size += installer.getIntegerProperty(
  103. "comp." + ids.elementAt(i) + ".real-size");
  104. components.addElement(installer.getProperty(
  105. "comp." + ids.elementAt(i) + ".fileset"));
  106. }
  107. }
  108. String installDir = chooseDirectory.installDir.getText();
  109. Map osTaskDirs = chooseDirectory.osTaskDirs;
  110. Iterator keys = osTaskDirs.keySet().iterator();
  111. while(keys.hasNext())
  112. {
  113. OperatingSystem.OSTask osTask = (OperatingSystem.OSTask)keys.next();
  114. String dir = ((JTextField)osTaskDirs.get(osTask)).getText();
  115. if(dir != null && dir.trim().length() != 0)
  116. {
  117. osTask.setEnabled(true);
  118. osTask.setDirectory(dir);
  119. }
  120. else
  121. osTask.setEnabled(false);
  122. }
  123. InstallThread thread = new InstallThread(
  124. installer,progress,
  125. installDir,osTasks,
  126. size,components);
  127. progress.setThread(thread);
  128. thread.start();
  129. }
  130. private void pageChanged()
  131. {
  132. switch(currentPage)
  133. {
  134. case 0:
  135. caption.setText("Installing " + appName);
  136. nextButton.setText("Next");
  137. prevButton.setEnabled(false);
  138. nextButton.setEnabled(true);
  139. break;
  140. case 1:
  141. caption.setText(installer.getProperty("app.license.title"));
  142. nextButton.setText("Next");
  143. prevButton.setEnabled(true);
  144. nextButton.setEnabled(true);
  145. break;
  146. case 2:
  147. caption.setText("Specify where " + appName
  148. + " is to be installed");
  149. nextButton.setText("Next");
  150. prevButton.setEnabled(true);
  151. if(!chooseDirectory.isOK())nextButton.setEnabled(false);
  152. break;
  153. case 3:
  154. caption.setText("Choose components to install");
  155. nextButton.setText("Install");
  156. prevButton.setEnabled(true);
  157. nextButton.setEnabled(true);
  158. break;
  159. case 4:
  160. caption.setText("Installing " + appName);
  161. nextButton.setText("Finish");
  162. prevButton.setEnabled(false);
  163. nextButton.setEnabled(false);
  164. install();
  165. break;
  166. case 5:
  167. caption.setText("Installation complete");
  168. nextButton.setText("Finish");
  169. prevButton.setEnabled(false);
  170. nextButton.setEnabled(true);
  171. cancelButton.setEnabled(false);
  172. break;
  173. }
  174. getRootPane().invalidate();
  175. getRootPane().validate();
  176. }
  177. class ActionHandler implements ActionListener
  178. {
  179. public void actionPerformed(ActionEvent evt)
  180. {
  181. Object source = evt.getSource();
  182. if(source == cancelButton)
  183. System.exit(0);
  184. else if(source == prevButton)
  185. {
  186. currentPage--;
  187. pageChanged();
  188. }
  189. else if(source == nextButton)
  190. {
  191. if(currentPage == pages.length - 1)
  192. System.exit(0);
  193. else
  194. {
  195. currentPage++;
  196. pageChanged();
  197. }
  198. }
  199. }
  200. }
  201. class WindowHandler extends WindowAdapter
  202. {
  203. public void windowClosing(WindowEvent evt)
  204. {
  205. System.exit(0);
  206. }
  207. }
  208. class WizardLayout implements LayoutManager
  209. {
  210. public void addLayoutComponent(String name, Component comp)
  211. {
  212. }
  213. public void removeLayoutComponent(Component comp)
  214. {
  215. }
  216. public Dimension preferredLayoutSize(Container parent)
  217. {
  218. Dimension dim = new Dimension();
  219. Dimension captionSize = caption.getPreferredSize();
  220. dim.width = captionSize.width;
  221. for(int i = 0; i < pages.length; i++)
  222. {
  223. Dimension _dim = pages[i].getPreferredSize();
  224. dim.width = Math.max(_dim.width,dim.width);
  225. dim.height = Math.max(_dim.height,dim.height);
  226. }
  227. dim.width += PADDING * 2;
  228. dim.height += PADDING * 2;
  229. dim.height += nextButton.getPreferredSize().height;
  230. dim.height += captionSize.height;
  231. return dim;
  232. }
  233. public Dimension minimumLayoutSize(Container parent)
  234. {
  235. return preferredLayoutSize(parent);
  236. }
  237. public void layoutContainer(Container parent)
  238. {
  239. Dimension size = parent.getSize();
  240. Dimension captionSize = caption.getPreferredSize();
  241. caption.setBounds(PADDING,PADDING,captionSize.width,
  242. captionSize.height);
  243. // make all buttons the same size
  244. Dimension buttonSize = cancelButton.getPreferredSize();
  245. buttonSize.width = Math.max(buttonSize.width,prevButton.getPreferredSize().width);
  246. buttonSize.width = Math.max(buttonSize.width,nextButton.getPreferredSize().width);
  247. // cancel button goes on far left
  248. cancelButton.setBounds(
  249. PADDING,
  250. size.height - buttonSize.height - PADDING,
  251. buttonSize.width,
  252. buttonSize.height);
  253. // prev and next buttons are on the right
  254. prevButton.setBounds(
  255. size.width - buttonSize.width * 2 - 6 - PADDING,
  256. size.height - buttonSize.height - PADDING,
  257. buttonSize.width,
  258. buttonSize.height);
  259. nextButton.setBounds(
  260. size.width - buttonSize.width - PADDING,
  261. size.height - buttonSize.height - PADDING,
  262. buttonSize.width,
  263. buttonSize.height);
  264. // calculate size for current page
  265. Rectangle currentPageBounds = new Rectangle();
  266. currentPageBounds.x = PADDING;
  267. currentPageBounds.y = PADDING * 2 + captionSize.height;
  268. currentPageBounds.width = size.width - currentPageBounds.x
  269. - PADDING;
  270. currentPageBounds.height = size.height - buttonSize.height
  271. - currentPageBounds.y - PADDING * 2;
  272. for(int i = 0; i < pages.length; i++)
  273. {
  274. Component page = pages[i];
  275. page.setBounds(currentPageBounds);
  276. page.setVisible(i == currentPage);
  277. }
  278. }
  279. }
  280. class TextPanel extends JPanel
  281. {
  282. TextPanel(String file)
  283. {
  284. super(new BorderLayout());
  285. JEditorPane text = new JEditorPane();
  286. try
  287. {
  288. text.setPage(TextPanel.this.getClass().getResource(file));
  289. }
  290. catch(Exception e)
  291. {
  292. text.setText("Error loading '" + file + "'");
  293. e.printStackTrace();
  294. }
  295. text.setEditable(false);
  296. JScrollPane scrollPane = new JScrollPane(text);
  297. Dimension dim = new Dimension();
  298. dim.width = 450;
  299. dim.height = 200;
  300. scrollPane.setPreferredSize(dim);
  301. TextPanel.this.add(BorderLayout.CENTER,scrollPane);
  302. }
  303. }
  304. class DirVerifier extends InputVerifier
  305. {
  306. private JTextComponent message;
  307. private Object pos;
  308. private JComponent parent;
  309. public DirVerifier(JComponent parent, Object pos)
  310. {
  311. super();
  312. message = new JTextArea(" ");
  313. message.setEditable(false);
  314. message.setBackground(parent.getBackground());
  315. this.parent = parent;
  316. this.pos = pos;
  317. }
  318. public boolean shouldYieldFocus(JComponent input)
  319. {
  320. return verify(input);
  321. }
  322. public boolean verify(JComponent input)
  323. {
  324. if(input instanceof JTextComponent)
  325. {
  326. String dir = ((JTextComponent)input).getText();
  327. if(checkNull(dir) &&
  328. checkExistNotDirectory(dir) &&
  329. checkExistNotEmpty(dir) &&
  330. checkRelative(dir))
  331. {
  332. //everything is perfect, clean label
  333. if(message.getParent()!=null)
  334. {
  335. SwingUtilities.invokeLater(new Runnable()
  336. {
  337. public void run()
  338. {
  339. parent.remove(message);
  340. parent.revalidate();
  341. parent.repaint();
  342. }
  343. });
  344. }
  345. }
  346. else
  347. {
  348. if(message.getParent()==null)
  349. {
  350. SwingUtilities.invokeLater(new Runnable()
  351. {
  352. public void run()
  353. {
  354. parent.add(message,pos);
  355. parent.revalidate();
  356. parent.repaint();
  357. }
  358. });
  359. }
  360. else message.repaint();
  361. }
  362. }
  363. return true;
  364. }
  365. private boolean checkNull(String file)
  366. {
  367. if(file.trim().length()==0)
  368. {
  369. message.setForeground(Color.red);
  370. message.setText(installer.getProperty("dir.null"));
  371. return false;
  372. }
  373. else return true;
  374. }
  375. private boolean checkRelative(String dir)
  376. {
  377. File f = new File(dir);
  378. if(!f.isAbsolute())
  379. {
  380. String msg = installer.getProperty("dir.relative");
  381. try
  382. {
  383. String full = f.getCanonicalPath();
  384. message.setForeground(Color.orange);
  385. message.setText(msg+'\n'+full);
  386. }
  387. catch(IOException ioe)
  388. {
  389. message.setForeground(Color.red);
  390. msg = installer.getProperty("dir.cant-resolve");
  391. message.setText(msg);
  392. }
  393. return false;
  394. }
  395. else return true;
  396. }
  397. private boolean checkExistNotDirectory(String dir)
  398. {
  399. File f = new File(dir);
  400. if(f.exists() && !f.isDirectory())
  401. {
  402. message.setForeground(Color.red);
  403. message.setText(installer.getProperty("dir.not-directory"));
  404. return false;
  405. }
  406. else return true;
  407. }
  408. private boolean checkExistNotEmpty(String dir)
  409. {
  410. File f = new File(dir);
  411. String[]cnt = f.list();
  412. if(cnt!=null && cnt.length>0)
  413. {
  414. message.setForeground(Color.orange);
  415. message.setText(installer.getProperty("dir.not-empty"));
  416. return false;
  417. }
  418. else return true;
  419. }
  420. }
  421. /**
  422. * only install directory is required, as it's necessary to scripts, etc.
  423. * If one uses the installer only for OS tasks, then one deselects all components
  424. * and that's all.
  425. */
  426. class ChooseDirectory extends JPanel
  427. {
  428. JTextField installDir;
  429. Map osTaskDirs;
  430. ChooseDirectory()
  431. {
  432. super(new BorderLayout());
  433. osTaskDirs = new HashMap();
  434. JPanel directoryPanel = new JPanel(new GridBagLayout());
  435. installDir = addField(directoryPanel,"Install program in:",
  436. OperatingSystem.getOperatingSystem()
  437. .getInstallDirectory(appName,appVersion));
  438. installDir.addFocusListener(new FocusAdapter()
  439. {
  440. public void focusLost(FocusEvent fe)
  441. {
  442. nextButton.setEnabled(isOK());
  443. }
  444. });
  445. for(int i = 0; i < osTasks.length; i++)
  446. {
  447. OperatingSystem.OSTask osTask = osTasks[i];
  448. String label = osTask.getLabel();
  449. if(label != null)
  450. {
  451. JTextField field = addField(directoryPanel,label,
  452. osTask.getDirectory());
  453. osTaskDirs.put(osTask,field);
  454. }
  455. }
  456. ChooseDirectory.this.add(BorderLayout.NORTH,directoryPanel);
  457. }
  458. boolean isOK()
  459. {
  460. if(installDir.getText().length()==0)return false;
  461. File f = new File(installDir.getText());
  462. return !(f.exists()&&!f.isDirectory());
  463. }
  464. private GridBagConstraints c = new GridBagConstraints();
  465. private JTextField addField(JPanel directoryPanel, String label,
  466. String defaultText)
  467. {
  468. //new line
  469. c.gridy++;
  470. //message
  471. JTextField field = new JTextField(defaultText);
  472. c.insets.bottom=3;
  473. c.gridx=0;
  474. c.gridwidth=3;
  475. c.insets.left=0;
  476. c.insets.right=0;
  477. c.anchor=GridBagConstraints.LINE_START;
  478. DirVerifier verif = new DirVerifier(directoryPanel,c.clone());
  479. field.setInputVerifier(verif);
  480. c.insets.bottom=12;
  481. c.gridx=0;
  482. c.gridy++;
  483. c.gridwidth=1;
  484. c.anchor=GridBagConstraints.LINE_END;
  485. directoryPanel.add(new JLabel(label,SwingConstants.RIGHT),c);
  486. c.gridx=1;
  487. c.fill=GridBagConstraints.HORIZONTAL;
  488. c.anchor=GridBagConstraints.CENTER;
  489. c.insets.left=12;
  490. c.insets.right=12;
  491. c.weightx=1.0;
  492. directoryPanel.add(field,c);
  493. JButton choose = new JButton("Choose...");
  494. choose.setRequestFocusEnabled(false);
  495. choose.addActionListener(new ActionHandler(field));
  496. c.gridx=2;
  497. c.insets.left=0;
  498. c.insets.right=0;
  499. c.fill=GridBagConstraints.NONE;
  500. c.weightx=0;
  501. directoryPanel.add(choose,c);
  502. return field;
  503. }
  504. class ActionHandler implements ActionListener
  505. {
  506. JTextField field;
  507. ActionHandler(JTextField field)
  508. {
  509. this.field = field;
  510. }
  511. public void actionPerformed(ActionEvent evt)
  512. {
  513. File directory = new File(field.getText());
  514. JFileChooser chooser = new JFileChooser(directory.getParent());
  515. chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
  516. chooser.setSelectedFile(directory);
  517. if(chooser.showOpenDialog(SwingInstall.this)
  518. == JFileChooser.APPROVE_OPTION)
  519. {
  520. field.setText(chooser.getSelectedFile().getPath());
  521. field.getInputVerifier().verify(field);
  522. }
  523. }
  524. }
  525. }
  526. class SelectComponents extends JPanel
  527. implements ActionListener
  528. {
  529. JPanel comp;
  530. JLabel sizeLabel;
  531. Vector filesets;
  532. SelectComponents()
  533. {
  534. super(new BorderLayout());
  535. comp = createCompPanel();
  536. SelectComponents.this.add(BorderLayout.NORTH,comp);
  537. sizeLabel = new JLabel("",SwingConstants.LEFT);
  538. SelectComponents.this.add(BorderLayout.SOUTH,sizeLabel);
  539. updateSize();
  540. }
  541. public void actionPerformed(ActionEvent evt)
  542. {
  543. updateSize();
  544. }
  545. private JPanel createCompPanel()
  546. {
  547. filesets = new Vector();
  548. int count = installer.getIntegerProperty("comp.count");
  549. JPanel panel = new JPanel(new GridLayout(count,1));
  550. String osClass = OperatingSystem.getOperatingSystem()
  551. .getClass().getName();
  552. osClass = osClass.substring(osClass.indexOf('$') + 1);
  553. for(int i = 0; i < count; i++)
  554. {
  555. String os = installer.getProperty("comp." + i + ".os");
  556. if(os != null && !osClass.equals(os))
  557. continue;
  558. JCheckBox checkBox = new JCheckBox(
  559. installer.getProperty("comp." + i + ".name")
  560. + " (" + installer.getProperty("comp." + i
  561. + ".disk-size") + "Kb)");
  562. checkBox.getModel().setSelected(true);
  563. checkBox.addActionListener(this);
  564. checkBox.setRequestFocusEnabled(false);
  565. filesets.addElement(new Integer(i));
  566. panel.add(checkBox);
  567. }
  568. Dimension dim = panel.getPreferredSize();
  569. dim.width = Integer.MAX_VALUE;
  570. panel.setMaximumSize(dim);
  571. return panel;
  572. }
  573. private void updateSize()
  574. {
  575. int size = 0;
  576. for(int i = 0; i < filesets.size(); i++)
  577. {
  578. if(((JCheckBox)comp.getComponent(i))
  579. .getModel().isSelected())
  580. {
  581. size += installer.getIntegerProperty("comp."
  582. + filesets.elementAt(i)
  583. + ".disk-size");
  584. }
  585. }
  586. sizeLabel.setText("Estimated disk usage of selected"
  587. + " components: " + size + "Kb");
  588. }
  589. }
  590. class SwingProgress extends JPanel implements Progress
  591. {
  592. JProgressBar progress;
  593. InstallThread thread;
  594. SwingProgress()
  595. {
  596. super(new BorderLayout());
  597. progress = new JProgressBar();
  598. progress.setStringPainted(true);
  599. SwingProgress.this.add(BorderLayout.NORTH,progress);
  600. }
  601. public void setMaximum(final int max)
  602. {
  603. SwingUtilities.invokeLater(new Runnable()
  604. {
  605. public void run()
  606. {
  607. progress.setMaximum(max);
  608. }
  609. });
  610. }
  611. public void advance(final int value)
  612. {
  613. try
  614. {
  615. SwingUtilities.invokeAndWait(new Runnable()
  616. {
  617. public void run()
  618. {
  619. progress.setValue(progress
  620. .getValue() + value);
  621. }
  622. });
  623. Thread.yield();
  624. }
  625. catch(Exception e)
  626. {
  627. }
  628. }
  629. public void done()
  630. {
  631. SwingUtilities.invokeLater(new Runnable()
  632. {
  633. public void run()
  634. {
  635. currentPage++;
  636. pageChanged();
  637. }
  638. });
  639. }
  640. public void error(final String message)
  641. {
  642. SwingUtilities.invokeLater(new Runnable()
  643. {
  644. public void run()
  645. {
  646. dispose();
  647. JOptionPane.showMessageDialog(null,
  648. message,
  649. "Installation aborted",
  650. JOptionPane.ERROR_MESSAGE);
  651. System.exit(1);
  652. }
  653. });
  654. }
  655. public void message(final String message)
  656. {
  657. SwingUtilities.invokeLater(new Runnable()
  658. {
  659. public void run()
  660. {
  661. progress.setString(message);
  662. }
  663. });
  664. }
  665. public void setThread(InstallThread thread)
  666. {
  667. this.thread = thread;
  668. }
  669. }
  670. }