/** * Copyright 2009 DFKI GmbH. * All Rights Reserved. Use is subject to license terms. * * This file is part of MARY TTS. * * MARY TTS is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ package marytts.tools.install; import java.awt.Dimension; import java.awt.Frame; import java.awt.HeadlessException; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.FilenameFilter; import java.io.PrintWriter; import java.io.StringWriter; import java.net.Authenticator; import java.net.MalformedURLException; import java.net.PasswordAuthentication; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import javax.swing.Box; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JOptionPane; import marytts.Version; import marytts.tools.install.ComponentDescription.Status; import marytts.util.MaryUtils; /** * * @author marc */ public class InstallerGUI extends javax.swing.JFrame implements VoiceUpdateListener { private Map<String, LanguageComponentDescription> languages; private Map<String, VoiceComponentDescription> voices; private LanguageComponentDescription currentLanguage = null; private String version = Version.specificationVersion(); /** Creates new form InstallerGUI */ public InstallerGUI() { this(null); } /** * Creates new installer gui and fills it with content from the given URL. * * @param maryComponentURL * maryComponentURL */ public InstallerGUI(String maryComponentURL) { this.languages = new TreeMap<String, LanguageComponentDescription>(); this.voices = new TreeMap<String, VoiceComponentDescription>(); initComponents(); customInitComponents(); if (maryComponentURL != null) { setAndUpdateFromMaryComponentURL(maryComponentURL); } } public void setAndUpdateFromMaryComponentURL(String maryComponentURL) { try { URL url = new URL(maryComponentURL); // if this doesn't fail then it's OK, we can set it tfComponentListURL.setText(maryComponentURL); updateFromMaryComponentURL(); } catch (MalformedURLException e) { // ignore, treat as unset value } } public void addLanguagesAndVoices(InstallFileParser p) { for (LanguageComponentDescription desc : p.getLanguageDescriptions()) { if (languages.containsKey(desc.getName())) { LanguageComponentDescription existing = languages.get(desc.getName()); // Check if one is an update of the other if (existing.getStatus() == Status.INSTALLED) { if (desc.isUpdateOf(existing)) { existing.setAvailableUpdate(desc); } } else if (desc.getStatus() == Status.INSTALLED) { languages.put(desc.getName(), desc); if (existing.isUpdateOf(desc)) { desc.setAvailableUpdate(existing); } } else { // both not installed: show only higher version number if (ComponentDescription.isVersionNewerThan(desc.getVersion(), existing.getVersion())) { languages.put(desc.getName(), desc); } // else leave existing as is } } else { // no such entry yet languages.put(desc.getName(), desc); } } for (VoiceComponentDescription desc : p.getVoiceDescriptions()) { if (voices.containsKey(desc.getName())) { VoiceComponentDescription existing = voices.get(desc.getName()); // Check if one is an update of the other if (existing.getStatus() == Status.INSTALLED) { if (desc.isUpdateOf(existing)) { existing.setAvailableUpdate(desc); } } else if (desc.getStatus() == Status.INSTALLED) { voices.put(desc.getName(), desc); if (existing.isUpdateOf(desc)) { desc.setAvailableUpdate(existing); } } else { // both not installed: show only higher version number if (ComponentDescription.isVersionNewerThan(desc.getVersion(), existing.getVersion())) { voices.put(desc.getName(), desc); } // else leave existing as is } } else { // no such entry yet voices.put(desc.getName(), desc); } } updateLanguagesTable(); } /** * This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The content of * this method is always regenerated by the Form Editor. */ // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents private void initComponents() { pDownload = new javax.swing.JPanel(); tfComponentListURL = new javax.swing.JTextField(); bUpdate = new javax.swing.JButton(); pInstallButtons = new javax.swing.JPanel(); bInstall = new javax.swing.JButton(); bUninstall = new javax.swing.JButton(); bUninstall1 = new javax.swing.JButton(); jPanel1 = new javax.swing.JPanel(); spLanguages = new javax.swing.JScrollPane(); pLanguages = new javax.swing.JPanel(); spVoices = new javax.swing.JScrollPane(); pVoices = new javax.swing.JPanel(); jLabel1 = new javax.swing.JLabel(); jLabel2 = new javax.swing.JLabel(); menuBar1 = new javax.swing.JMenuBar(); menuTools1 = new javax.swing.JMenu(); miProxy1 = new javax.swing.JMenuItem(); setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE); setTitle("MARY TTS Installer"); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosing(java.awt.event.WindowEvent evt) { InstallerGUI.this.windowClosing(evt); } }); pDownload.setBorder(javax.swing.BorderFactory.createTitledBorder("Download languages and voices from:")); // hack so that SVN checkout from "trunk" will look for "latest" directory on server: if (version.equals("trunk")) { version = "latest"; } tfComponentListURL.setText("https://raw.github.com/marytts/marytts/master/download/marytts-components.xml"); tfComponentListURL.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { tfComponentListURLActionPerformed(evt); } }); bUpdate.setText("Update"); bUpdate.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { bUpdateActionPerformed(evt); } }); org.jdesktop.layout.GroupLayout pDownloadLayout = new org.jdesktop.layout.GroupLayout(pDownload); pDownload.setLayout(pDownloadLayout); pDownloadLayout.setHorizontalGroup(pDownloadLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING).add( pDownloadLayout .createSequentialGroup() .add(tfComponentListURL, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 540, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 73, Short.MAX_VALUE).add(bUpdate) .addContainerGap())); pDownloadLayout.setVerticalGroup(pDownloadLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING).add( pDownloadLayout .createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) .add(tfComponentListURL, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(bUpdate))); bInstall.setText("Install selected"); bInstall.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { bInstallActionPerformed(evt); } }); bUninstall.setText("Uninstall selected"); bUninstall.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { bUninstallActionPerformed(evt); } }); bUninstall1.setText("Quit"); bUninstall1.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { quitActionPerformed(evt); } }); org.jdesktop.layout.GroupLayout pInstallButtonsLayout = new org.jdesktop.layout.GroupLayout(pInstallButtons); pInstallButtons.setLayout(pInstallButtonsLayout); pInstallButtonsLayout.setHorizontalGroup(pInstallButtonsLayout.createParallelGroup( org.jdesktop.layout.GroupLayout.LEADING).add( org.jdesktop.layout.GroupLayout.TRAILING, pInstallButtonsLayout .createSequentialGroup() .add(146, 146, 146) .add(bInstall, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE).add(14, 14, 14).add(bUninstall) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED).add(bUninstall1).add(194, 194, 194))); pInstallButtonsLayout.setVerticalGroup(pInstallButtonsLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(pInstallButtonsLayout .createSequentialGroup() .addContainerGap() .add(pInstallButtonsLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE).add(bUninstall) .add(bInstall).add(bUninstall1)) .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))); spLanguages.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); pLanguages.setLayout(new javax.swing.BoxLayout(pLanguages, javax.swing.BoxLayout.Y_AXIS)); spLanguages.setViewportView(pLanguages); spVoices.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); pVoices.setLayout(new javax.swing.BoxLayout(pVoices, javax.swing.BoxLayout.Y_AXIS)); spVoices.setViewportView(pVoices); jLabel1.setText("Languages"); jLabel2.setText("Voices"); org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING).add( jPanel1Layout .createSequentialGroup() .add(jPanel1Layout .createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(spLanguages, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 340, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE).add(jLabel1)) .add(21, 21, 21) .add(jPanel1Layout .createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(spVoices, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 369, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE).add(jLabel2)).addContainerGap())); jPanel1Layout.setVerticalGroup(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING).add( org.jdesktop.layout.GroupLayout.TRAILING, jPanel1Layout .createSequentialGroup() .addContainerGap() .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE).add(jLabel1) .add(jLabel2)) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(spVoices, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 303, Short.MAX_VALUE) .add(spLanguages, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 303, Short.MAX_VALUE)))); menuTools1.setText("Tools"); miProxy1.setText("Proxy settings..."); miProxy1.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { miProxy1ActionPerformed(evt); } }); menuTools1.add(miProxy1); menuBar1.add(menuTools1); setJMenuBar(menuBar1); org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING).add( org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup() .addContainerGap() .add(layout .createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING) .add(org.jdesktop.layout.GroupLayout.LEADING, jPanel1, 0, 730, Short.MAX_VALUE) .add(org.jdesktop.layout.GroupLayout.LEADING, pInstallButtons, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(org.jdesktop.layout.GroupLayout.LEADING, pDownload, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)).addContainerGap())); layout.setVerticalGroup(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING).add( layout.createSequentialGroup() .add(pDownload, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(jPanel1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(pInstallButtons, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))); pack(); }// </editor-fold>//GEN-END:initComponents private void tfComponentListURLActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_tfComponentListURLActionPerformed updateFromMaryComponentURL(); }// GEN-LAST:event_tfComponentListURLActionPerformed private void miProxy1ActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_miProxy1ActionPerformed ProxyPanel prp = new ProxyPanel(System.getProperty("http.proxyHost"), System.getProperty("http.proxyPort")); final JOptionPane optionPane = new JOptionPane(prp, JOptionPane.PLAIN_MESSAGE, JOptionPane.YES_NO_OPTION, null, new String[] { "OK", "Cancel" }, "OK"); final JDialog dialog = new JDialog((Frame) null, "", true); dialog.setContentPane(optionPane); optionPane.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { String prop = e.getPropertyName(); if (dialog.isVisible() && (e.getSource() == optionPane) && (prop.equals(JOptionPane.VALUE_PROPERTY))) { dialog.setVisible(false); } } }); dialog.pack(); dialog.setVisible(true); if ("OK".equals(optionPane.getValue())) { System.setProperty("http.proxyHost", prp.getProxyHost()); System.setProperty("http.proxyPort", prp.getProxyPort()); } }// GEN-LAST:event_miProxy1ActionPerformed private void bUninstallActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_bUninstallActionPerformed uninstallSelectedLanguagesAndVoices(); }// GEN-LAST:event_bUninstallActionPerformed private void bInstallActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_bInstallActionPerformed installSelectedLanguagesAndVoices(); }// GEN-LAST:event_bInstallActionPerformed private void bUpdateActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_bUpdateActionPerformed updateFromMaryComponentURL(); }// GEN-LAST:event_bUpdateActionPerformed private void updateFromMaryComponentURL() throws HeadlessException { String urlString = tfComponentListURL.getText().trim().replaceAll(" ", "%20"); try { URL url = new URL(urlString); InstallFileParser p = new InstallFileParser(url); addLanguagesAndVoices(p); } catch (Exception e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); pw.close(); String message = sw.toString(); JOptionPane.showMessageDialog(this, "Problem retrieving component list:\n" + message); } } private void windowClosing(java.awt.event.WindowEvent evt) {// GEN-FIRST:event_windowClosing confirmExit(); }// GEN-LAST:event_windowClosing private void quitActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_quitActionPerformed confirmExit(); }// GEN-LAST:event_quitActionPerformed private void customInitComponents() { bUpdate.requestFocusInWindow(); // Set up the authentication dialog in case it will be used: Authenticator.setDefault(new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { PasswordPanel passP = new PasswordPanel(); final JOptionPane optionPane = new JOptionPane(passP, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION, null, new String[] { "OK", "Cancel" }, "OK"); final JDialog dialog = new JDialog((Frame) null, "", true); dialog.setContentPane(optionPane); optionPane.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { String prop = e.getPropertyName(); if (dialog.isVisible() && (e.getSource() == optionPane) && (prop.equals(JOptionPane.VALUE_PROPERTY))) { dialog.setVisible(false); } } }); dialog.pack(); dialog.setVisible(true); if ("OK".equals(optionPane.getValue())) { return new PasswordAuthentication(passP.getUser(), passP.getPassword()); } return null; } }); } private void updateLanguagesTable() { pLanguages.removeAll(); for (String dName : languages.keySet()) { ComponentDescription desc = languages.get(dName); pLanguages.add(new ShortDescriptionPanel(desc, this)); } pLanguages.add(Box.createVerticalGlue()); if (languages.size() > 0) { pLanguages.getComponent(0).requestFocusInWindow(); updateVoices(languages.get(languages.keySet().iterator().next()), true); } } public void updateVoices(LanguageComponentDescription newLanguage, boolean forceUpdate) { if (currentLanguage != null && currentLanguage.equals(newLanguage) && !forceUpdate) { return; } currentLanguage = newLanguage; List<VoiceComponentDescription> lVoices = getVoicesForLanguage(currentLanguage); pVoices.removeAll(); for (ComponentDescription desc : lVoices) { pVoices.add(new ShortDescriptionPanel(desc, null)); } pVoices.add(Box.createVerticalGlue()); pVoices.repaint(); this.pack(); } private HashSet<ComponentDescription> getAllInstalledComponents() { HashSet<ComponentDescription> components = new HashSet<ComponentDescription>(); for (ComponentDescription component : languages.values()) { if (component.getStatus().equals(Status.INSTALLED)) { components.add(component); } } for (ComponentDescription component : voices.values()) { if (component.getStatus().equals(Status.INSTALLED)) { components.add(component); } } return components; } private List<VoiceComponentDescription> getVoicesForLanguage(LanguageComponentDescription language) { List<VoiceComponentDescription> lVoices = new ArrayList<VoiceComponentDescription>(); for (String vName : voices.keySet()) { VoiceComponentDescription v = voices.get(vName); if (v.getDependsLanguage().equals(language.getName())) { lVoices.add(v); } } return lVoices; } private void confirmExit() { if (getComponentsSelectedForInstallation().size() + getComponentsSelectedForUninstall().size() == 0) { // Exit without further ado this.setVisible(false); System.exit(0); } int choice = JOptionPane .showConfirmDialog(this, "Discard selection and exit?", "Exit program", JOptionPane.YES_NO_OPTION); if (choice == JOptionPane.YES_OPTION) { this.setVisible(false); System.exit(0); } } private List<ComponentDescription> getComponentsSelectedForInstallation() { List<ComponentDescription> toInstall = new ArrayList<ComponentDescription>(); for (String langName : languages.keySet()) { LanguageComponentDescription lang = languages.get(langName); if (lang.isSelected() && (lang.getStatus() != Status.INSTALLED || lang.isUpdateAvailable())) { toInstall.add(lang); System.out.println(lang.getName() + " selected for installation"); } // Show voices with corresponding language: List<VoiceComponentDescription> lVoices = getVoicesForLanguage(lang); for (VoiceComponentDescription voice : lVoices) { if (voice.isSelected() && (voice.getStatus() != Status.INSTALLED || voice.isUpdateAvailable())) { toInstall.add(voice); System.out.println(voice.getName() + " selected for installation"); } } } return toInstall; } public void installSelectedLanguagesAndVoices() { long downloadSize = 0; List<ComponentDescription> toInstall = getComponentsSelectedForInstallation(); if (toInstall.size() == 0) { JOptionPane.showMessageDialog(this, "You have not selected any installable components"); return; } // Verify if all dependencies are met // There are the following ways of meeting a dependency: // - the component with the right name and version number is already installed; // - the component with the right name and version number is selected for installation; // - an update of the component with the right version number is selected for installation. Map<String, String> unmetDependencies = new TreeMap<String, String>(); // map name to problem description for (ComponentDescription cd : toInstall) { if (cd instanceof VoiceComponentDescription) { // Currently have dependencies only for voice components VoiceComponentDescription vcd = (VoiceComponentDescription) cd; String depLang = vcd.getDependsLanguage(); String depVersion = vcd.getDependsVersion(); // Two options for fulfilling the dependency: either it is already installed, or it is in toInstall LanguageComponentDescription lcd = languages.get(depLang); if (lcd == null) { unmetDependencies.put(depLang, "-- no such language component"); } else if (lcd.getStatus() == Status.INSTALLED) { if (ComponentDescription.isVersionNewerThan(depVersion, lcd.getVersion())) { ComponentDescription update = lcd.getAvailableUpdate(); if (update == null) { unmetDependencies.put(depLang, "version " + depVersion + " is required by " + vcd.getName() + ",\nbut older version " + lcd.getVersion() + " is installed and no update is available"); } else if (ComponentDescription.isVersionNewerThan(depVersion, update.getVersion())) { unmetDependencies.put(depLang, "version " + depVersion + " is required by " + vcd.getName() + ",\nbut only version " + update.getVersion() + " is available as an update"); } else if (!toInstall.contains(lcd)) { unmetDependencies.put(depLang, "version " + depVersion + " is required by " + vcd.getName() + ",\nbut older version " + lcd.getVersion() + " is installed\nand update to version " + update.getVersion() + " is not selected for installation"); } } } else if (!toInstall.contains(lcd)) { if (ComponentDescription.isVersionNewerThan(depVersion, lcd.getVersion())) { unmetDependencies.put(depLang, "version " + depVersion + " is required by " + vcd.getName() + ",\nbut only older version " + lcd.getVersion() + " is available"); } else { unmetDependencies.put(depLang, "is required by " + vcd.getName() + "\nbut is not selected for installation"); } } } } // Any unmet dependencies? if (unmetDependencies.size() > 0) { StringBuilder buf = new StringBuilder(); for (String compName : unmetDependencies.keySet()) { buf.append("Component ").append(compName).append(" ").append(unmetDependencies.get(compName)).append("\n"); } JOptionPane.showMessageDialog(this, buf.toString(), "Dependency problem", JOptionPane.WARNING_MESSAGE); return; } for (ComponentDescription cd : toInstall) { if (cd.getStatus() == Status.AVAILABLE) { downloadSize += cd.getPackageSize(); } else if (cd.getStatus() == Status.INSTALLED && cd.isUpdateAvailable()) { if (cd.getAvailableUpdate().getStatus() == Status.AVAILABLE) { downloadSize += cd.getAvailableUpdate().getPackageSize(); } } } int returnValue = JOptionPane .showConfirmDialog(this, "Install " + toInstall.size() + " components?\n(" + MaryUtils.toHumanReadableSize(downloadSize) + " to download)", "Proceed with installation?", JOptionPane.YES_NO_OPTION); if (returnValue != JOptionPane.YES_OPTION) { System.err.println("Aborting installation."); return; } System.out.println("Check license(s)"); boolean accepted = showLicenses(toInstall); if (accepted) { System.out.println("Starting installation"); showProgressPanel(toInstall, true); } } /** * Show the licenses for the components in toInstall * * @param toInstall * the components to install * @return true if all licenses were accepted, false otherwise */ private boolean showLicenses(List<ComponentDescription> toInstall) { Map<URL, SortedSet<ComponentDescription>> licenseGroups = new HashMap<URL, SortedSet<ComponentDescription>>(); // Group components by their license: for (ComponentDescription cd : toInstall) { URL licenseURL = cd.getLicenseURL(); // may be null // null is an acceptable key for HashMaps, so it's OK. SortedSet<ComponentDescription> compsUnderLicense = licenseGroups.get(licenseURL); if (compsUnderLicense == null) { compsUnderLicense = new TreeSet<ComponentDescription>(); licenseGroups.put(licenseURL, compsUnderLicense); } assert compsUnderLicense != null; compsUnderLicense.add(cd); } // Now show license for each group for (URL licenseURL : licenseGroups.keySet()) { if (licenseURL == null) { continue; } URL localURL = LicenseRegistry.getLicense(licenseURL); SortedSet<ComponentDescription> comps = licenseGroups.get(licenseURL); System.out.println("Showing license " + licenseURL + " for " + comps.size() + " components"); LicensePanel licensePanel = new LicensePanel(localURL, comps); final JOptionPane optionPane = new JOptionPane(licensePanel, JOptionPane.PLAIN_MESSAGE, JOptionPane.YES_NO_OPTION, null, new String[] { "Reject", "Accept" }, "Reject"); optionPane.setPreferredSize(new Dimension(800, 600)); final JDialog dialog = new JDialog((Frame) null, "Do you accept the following license?", true); dialog.setContentPane(optionPane); optionPane.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { String prop = e.getPropertyName(); if (dialog.isVisible() && (e.getSource() == optionPane) && (prop.equals(JOptionPane.VALUE_PROPERTY))) { dialog.setVisible(false); } } }); dialog.pack(); dialog.setVisible(true); if (!"Accept".equals(optionPane.getValue())) { System.out.println("License not accepted. Installation of component cannot proceed."); return false; } System.out.println("License accepted."); } return true; } private List<ComponentDescription> getComponentsSelectedForUninstall() { List<ComponentDescription> toUninstall = new ArrayList<ComponentDescription>(); for (String langName : languages.keySet()) { LanguageComponentDescription lang = languages.get(langName); if (lang.isSelected() && lang.getStatus() == Status.INSTALLED) { toUninstall.add(lang); System.out.println(lang.getName() + " selected for uninstall"); } // Show voices with corresponding language: List<VoiceComponentDescription> lVoices = getVoicesForLanguage(lang); for (VoiceComponentDescription voice : lVoices) { if (voice.isSelected() && voice.getStatus() == Status.INSTALLED) { toUninstall.add(voice); System.out.println(voice.getName() + " selected for uninstall"); } } } findAndStoreSharedFiles(toUninstall); return toUninstall; } /** * For all components to be uninstalled, find any shared files required by components that will <i>not</i> be uninstalled, and * store them in the component (using {@link ComponentDescription#setSharedFiles(List)}). * {@link ComponentDescription#uninstall()} can then check and refrain from removing those shared files. * * @param uninstallComponents * selected for uninstallation */ private void findAndStoreSharedFiles(List<ComponentDescription> uninstallComponents) { // first, find out which components are *not* selected for removal: Set<ComponentDescription> retainComponents = getAllInstalledComponents(); retainComponents.removeAll(uninstallComponents); // if all components are selected for removal, there is nothing to do here: if (retainComponents.isEmpty()) { return; } // otherwise, list all unique files required by retained components: Set<String> retainFiles = new TreeSet<String>(); for (ComponentDescription retainComponent : retainComponents) { retainFiles.addAll(retainComponent.getInstalledFileNames()); } // finally, store shared files in components to be removed (queried later): for (ComponentDescription uninstallComponent : uninstallComponents) { Set<String> sharedFiles = new HashSet<String>(uninstallComponent.getInstalledFileNames()); sharedFiles.retainAll(retainFiles); if (!sharedFiles.isEmpty()) { uninstallComponent.setSharedFiles(sharedFiles); } } } public void uninstallSelectedLanguagesAndVoices() { List<ComponentDescription> toUninstall = getComponentsSelectedForUninstall(); if (toUninstall.size() == 0) { JOptionPane.showMessageDialog(this, "You have not selected any uninstallable components"); return; } int returnValue = JOptionPane.showConfirmDialog(this, "Uninstall " + toUninstall.size() + " components?\n", "Proceed with uninstall?", JOptionPane.YES_NO_OPTION); if (returnValue != JOptionPane.YES_OPTION) { System.err.println("Aborting uninstall."); return; } System.out.println("Starting uninstall"); showProgressPanel(toUninstall, false); } private void showProgressPanel(List<ComponentDescription> comps, boolean install) { final ProgressPanel pp = new ProgressPanel(comps, install); final JOptionPane optionPane = new JOptionPane(pp, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION, null, new String[] { "Abort" }, "Abort"); // optionPane.setPreferredSize(new Dimension(640,480)); final JDialog dialog = new JDialog((Frame) null, "Progress", false); dialog.setContentPane(optionPane); optionPane.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { String prop = e.getPropertyName(); if (dialog.isVisible() && (e.getSource() == optionPane) && (prop.equals(JOptionPane.VALUE_PROPERTY))) { pp.requestExit(); dialog.setVisible(false); } } }); dialog.pack(); dialog.setVisible(true); new Thread(pp).start(); } /** * @param args * the command line arguments * @throws Exception * Exception */ public static void main(String args[]) throws Exception { String maryBase = System.getProperty("mary.base"); if (maryBase == null || !new File(maryBase).isDirectory()) { JFrame window = new JFrame("This is the Frames's Title Bar!"); JFileChooser fc = new JFileChooser(); fc.setDialogTitle("Please indicate MARY TTS installation directory"); fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int returnVal = fc.showOpenDialog(window); if (returnVal == JFileChooser.APPROVE_OPTION) { File file = fc.getSelectedFile(); if (file != null) maryBase = file.getAbsolutePath(); } } if (maryBase == null || !new File(maryBase).isDirectory()) { System.out.println("No MARY base directory -- exiting."); System.exit(0); } System.setProperty("mary.base", maryBase); File archiveDir = new File(maryBase + "/download"); if (!archiveDir.exists()) archiveDir.mkdir(); System.setProperty("mary.downloadDir", archiveDir.getPath()); File infoDir = new File(maryBase + "/installed"); if (!infoDir.exists()) infoDir.mkdir(); System.setProperty("mary.installedDir", infoDir.getPath()); InstallerGUI g = new InstallerGUI(); File[] componentDescriptionFiles = infoDir.listFiles(new FilenameFilter() { public boolean accept(File dir, String name) { return name.endsWith(".xml"); } }); for (File cd : componentDescriptionFiles) { try { g.addLanguagesAndVoices(new InstallFileParser(cd.toURI().toURL())); } catch (Exception exc) { exc.printStackTrace(); } } componentDescriptionFiles = archiveDir.listFiles(new FilenameFilter() { public boolean accept(File dir, String name) { return name.endsWith(".xml"); } }); for (File cd : componentDescriptionFiles) { try { g.addLanguagesAndVoices(new InstallFileParser(cd.toURI().toURL())); } catch (Exception exc) { exc.printStackTrace(); } } if (args.length > 0) { g.setAndUpdateFromMaryComponentURL(args[0]); } g.setVisible(true); } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton bInstall; private javax.swing.JButton bUninstall; private javax.swing.JButton bUninstall1; private javax.swing.JButton bUpdate; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JPanel jPanel1; private javax.swing.JMenuBar menuBar1; private javax.swing.JMenu menuTools1; private javax.swing.JMenuItem miProxy1; private javax.swing.JPanel pDownload; private javax.swing.JPanel pInstallButtons; private javax.swing.JPanel pLanguages; private javax.swing.JPanel pVoices; private javax.swing.JScrollPane spLanguages; private javax.swing.JScrollPane spVoices; private javax.swing.JTextField tfComponentListURL; // End of variables declaration//GEN-END:variables }