package org.limewire.ui.swing.util; import java.awt.Dialog.ModalityType; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.JToggleButton; import net.miginfocom.swing.MigLayout; import org.limewire.core.api.download.DownloadAction; import org.limewire.core.api.download.DownloadException; import org.limewire.core.api.download.SaveLocationManager; import org.limewire.logging.Log; import org.limewire.logging.LogFactory; import org.limewire.ui.swing.action.UrlAction; import org.limewire.ui.swing.components.FocusJOptionPane; import org.limewire.ui.swing.components.HyperlinkButton; import org.limewire.ui.swing.components.LimeJDialog; import org.limewire.ui.swing.components.MultiLineLabel; import org.limewire.ui.swing.settings.SwingUiSettings; import org.limewire.util.FileUtils; import com.google.inject.Inject; /** * A universal handler for DownloadExceptions generated while * performing downloads. */ public class DownloadExceptionHandlerImpl implements DownloadExceptionHandler { private static final Log LOG = LogFactory.getLog(DownloadExceptionHandlerImpl.class); private final SaveLocationManager saveLocationManager; /** * Constructs a DownloadExceptionHandler with the specified * SaveLocationManager. */ @Inject public DownloadExceptionHandlerImpl(SaveLocationManager saveLocationManager) { this.saveLocationManager = saveLocationManager; } /** * Handles the supplied DownloadException. The method may take one of * several actions: eat the exception, try downloading again using the * supplied <code>downloadAction</code>, or popup a dialog to try and save * the download in a new location. * * @param supportNewSaveFileName - Indicates that the downloader supports a * new saveFileName. If true the user will have the option of picking * a new file name, provided they do not have the setting to * automatically rename the file turned on. Otherwise if it does not * support a new file name, a directory chooser is opened that will * allow the */ public void handleDownloadException(final DownloadAction downLoadAction, final DownloadException e, final boolean supportNewSaveFileName) { // Create Runnable to execute task on UI thread. This is necessary // if the handler method has been invoked from a background thread. SwingUtils.invokeLater(new Runnable() { public void run() { handleException(downLoadAction, e, supportNewSaveFileName); } }); } /** * Handles the specified DownloadException. The method may prompt the * user for input, and should be executed from the UI thread. */ private void handleException(final DownloadAction downLoadAction, final DownloadException e, final boolean supportNewSaveFileName) { if (e.getErrorCode() == DownloadException.ErrorCode.DOWNLOAD_CANCELLED) { //no error to handle. downLoadAction.downloadCanceled(e); return; } if (e.getErrorCode() == DownloadException.ErrorCode.FILE_ALREADY_DOWNLOADING) { // ignore, just return because we are already downloading this file downLoadAction.downloadCanceled(e); showErrorMessage(e); return; } // check to make sure this is a DownloadException we can handle if ((e.getErrorCode() != DownloadException.ErrorCode.FILE_ALREADY_EXISTS) && (e.getErrorCode() != DownloadException.ErrorCode.FILE_IS_ALREADY_DOWNLOADED_TO)) { // Create user message. downLoadAction.downloadCanceled(e); showErrorMessage(e); return; } else if (e.getErrorCode() == DownloadException.ErrorCode.FILE_IS_ALREADY_DOWNLOADED_TO && !supportNewSaveFileName) { // prevents infinite loop case where for bit torrent files we can't // change the save file at the moment downLoadAction.downloadCanceled(e); showErrorMessage(e); return; } // select a save file name File saveFile = null; if (supportNewSaveFileName && SwingUiSettings.AUTO_RENAME_DUPLICATE_FILES.getValue()) { saveFile = getAutoSaveFile(e); } else { if (supportNewSaveFileName) { saveFile = FileChooser.getSaveAsFile(GuiUtils.getMainFrame(), I18n .tr("Save File As..."), e.getFile()); } else { saveFile = e.getFile(); if (saveFile != null && saveFile.exists()) { createOverwriteDialogue(saveFile, downLoadAction, e, supportNewSaveFileName); return; } } if (saveFile == null) { // null saveFile means user selected cancel downLoadAction.downloadCanceled(e); return; } } // if the file already exists at this point, the user has already agreed // to overwrite it download(downLoadAction, supportNewSaveFileName, saveFile, saveFile.exists()); } private void showErrorMessage(final DownloadException e) { Object message = null; switch (e.getErrorCode()) { case FILE_ALREADY_DOWNLOADING: message = I18n.tr("Sorry, this file is already being downloaded."); break; case DIRECTORY_NOT_WRITEABLE: case DIRECTORY_DOES_NOT_EXIST: case NOT_A_DIRECTORY: case PATH_NAME_TOO_LONG: message = I18n.tr("Sorry, you can't download files to this location."); break; case NO_TORRENT_MANAGER: MultiLineLabel label = new MultiLineLabel(I18n.tr( "Sorry, there is a problem with torrents.\nPlease try reinstalling LimeWire to solve this problem.")); HyperlinkButton help = new HyperlinkButton(I18n.tr( "You can also ask for help on the forums.")); help.addActionListener(new UrlAction( "http://www.limewire.com/client_redirect/?page=bitTorrentBugs")); JPanel panel = new JPanel(); panel.setOpaque(false); panel.setLayout(new MigLayout("gapy 15")); panel.add(label, "wrap"); panel.add(help, "wrap"); panel.validate(); message = panel; break; case FILES_STILL_RESUMING: message = I18n.tr("Sorry, we are still loading your old downloads.\nPlease wait to add a new download until we are done."); break; default: message = I18n.tr("Sorry, there was a problem downloading your file."); break; } // Log exception and display user message. LOG.error(message, e); FocusJOptionPane.showMessageDialog(GuiUtils.getMainFrame(), message, I18n.tr("Download"), JOptionPane.INFORMATION_MESSAGE); } /** * Iterates through possible file names until an available one is found and * is returned. */ private File getAutoSaveFile(final DownloadException e) { File saveFile; saveFile = e.getFile(); int index = 1; String fileName = FileUtils.getFilenameNoExtension(saveFile.getName()); String extension = FileUtils.getFileExtension(saveFile); while (saveFile.exists() || saveLocationManager.isSaveLocationTaken(saveFile)) { String newFileName = fileName + "(" + index + ")"; if (extension.length() > 0) { newFileName += "." + extension; } saveFile = new File(saveFile.getParentFile(), newFileName); index++; } return saveFile; } /** * Downloads the given file using the supplied download action and handles * any possible DownloadExceptions. */ private void download(final DownloadAction downLoadAction, final boolean supportNewSaveFileName, File saveFile, boolean overwrite) { File newSaveFile = supportNewSaveFileName ? saveFile : saveFile.getParentFile(); try { downLoadAction.download(newSaveFile, overwrite); } catch (DownloadException e1) { handleDownloadException(downLoadAction, e1, supportNewSaveFileName); } } private void createOverwriteDialogue(final File overwriteFile, final DownloadAction downLoadAction, final DownloadException ex, final boolean supportNewSaveFileName) { final JDialog dialog = new LimeJDialog(GuiUtils.getMainFrame()); dialog.setModalityType(ModalityType.APPLICATION_MODAL); final MultiLineLabel message = new MultiLineLabel(I18n .tr("File already exists. What do you want to do?"), 400); final JTextField filePathField = new JTextField(25); filePathField.setEnabled(false); filePathField.setText(overwriteFile.getAbsolutePath()); JToggleButton overwriteButton = null; overwriteButton = new JToggleButton(I18n.tr("Overwrite")); overwriteButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { dialog.dispose(); download(downLoadAction, supportNewSaveFileName, overwriteFile, true); } }); JButton cancelButton = new JButton(I18n.tr("Cancel")); cancelButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { dialog.dispose(); File oldSaveFile = overwriteFile; File oldSaveFileParent = overwriteFile.getParentFile() != null ? overwriteFile.getParentFile() : overwriteFile; File newSaveParent = FileChooser.getInputDirectory(GuiUtils.getMainFrame(), I18n.tr("Choose a new folder to save download."), I18n.tr("Select"), oldSaveFileParent); if (newSaveParent != null && new File(newSaveParent, oldSaveFile.getName()).exists()) { File saveFile = new File(newSaveParent, oldSaveFile.getName()); createOverwriteDialogue(saveFile, downLoadAction, ex, supportNewSaveFileName); return; } else if(newSaveParent != null) { File saveFile = new File(newSaveParent, oldSaveFile.getName()); download(downLoadAction, supportNewSaveFileName, saveFile, false); } } }); JPanel panel = new JPanel(); panel.setLayout(new MigLayout("hidemode 3, gapy 10", "", "")); panel.add(message, "span 2, wrap"); panel.add(filePathField, "grow x, push, wrap"); panel.add(overwriteButton, "alignx right"); panel.add(cancelButton); dialog.setContentPane(panel); dialog.pack(); dialog.setLocationRelativeTo(GuiUtils.getMainFrame()); dialog.setVisible(true); } }