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