package org.limewire.ui.swing.properties; import static org.limewire.ui.swing.util.I18n.tr; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.JButton; import javax.swing.JPanel; import net.miginfocom.swing.MigLayout; import org.jdesktop.application.Resource; import org.limewire.bittorrent.Torrent; import org.limewire.core.api.FilePropertyKey; import org.limewire.core.api.download.DownloadItem; import org.limewire.core.api.library.LibraryManager; import org.limewire.core.api.library.LocalFileItem; import org.limewire.core.api.library.PropertiableFile; import org.limewire.ui.swing.action.AbstractAction; import org.limewire.ui.swing.components.LimeJDialog; import org.limewire.ui.swing.properties.FileInfoTabPanel.FileInfoTabListener; import org.limewire.ui.swing.properties.FileInfoTabPanel.Tabs; import org.limewire.ui.swing.util.GuiUtils; import org.limewire.ui.swing.util.I18n; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; /** * A Dialog for displaying FileInfo about a file. This dialog changes * depending on the state of the file and the location of the file. */ public class FileInfoDialog extends LimeJDialog { /** * Type of FileInfoDialog that can be created. */ public static enum FileInfoType { /** * File exists within the current LimeWire. */ LOCAL_FILE, /** * File is in the process of downloading or a portion * of the file has been downloaded already, */ DOWNLOADING_FILE, /** * File it contained on one or more networked computers but not * within this client. */ REMOTE_FILE, /** * The file represents a current upload. */ UPLOADING_FILE; } @Resource private Color backgroundColor; private final LibraryManager libraryManager; private final FileInfoTabPanel tabPanel; private final FileInfoOverviewPanel overviewPanel; private final JPanel cardPanel; private final Map<Tabs, FileInfoPanel> cards; private JButton okButton; private PropertiableFile propertiableFile; private RenameListener renameListener; @Inject public FileInfoDialog(@Assisted PropertiableFile propertiable, @Assisted final FileInfoType type, FileInfoTabPanel fileInfoTabPanel, final FileInfoPanelFactory fileInfoFactory, LibraryManager libraryManager) { super(GuiUtils.getMainFrame()); this.libraryManager = libraryManager; tabPanel = fileInfoTabPanel; cardPanel = new JPanel(new BorderLayout()); cardPanel.setPreferredSize(new Dimension(400,600)); cards = new HashMap<Tabs, FileInfoPanel>(); this.propertiableFile = propertiable; GuiUtils.assignResources(this); cardPanel.setOpaque(false); createTabs(propertiableFile, type); cards.put(Tabs.GENERAL, fileInfoFactory.createGeneralPanel(type, propertiableFile)); cardPanel.add(cards.get(Tabs.GENERAL).getComponent()); updateTitle(propertiableFile.getFileName()); setLayout(new MigLayout("gap 0, insets 0, fill")); getContentPane().setBackground(backgroundColor); overviewPanel = (FileInfoOverviewPanel) fileInfoFactory.createOverviewPanel(type, propertiableFile); add(overviewPanel.getComponent(), "dock north"); add(tabPanel.getComponent(), "dock north"); add(cardPanel, "gapleft 10, grow, gapright 10"); createFooter(); setPreferredSize(new Dimension(500,565)); setDefaultCloseOperation(FileInfoDialog.DISPOSE_ON_CLOSE); if (type == FileInfoType.LOCAL_FILE) { // TODO: In KDE there is a problem where minimise is possible and // a minimised modal dialogue can't be restored. The dialogue // disappears and control can never be restored to the rest of the // app. Stopping resizability prevents this problem. setModalityType(ModalityType.APPLICATION_MODAL); } pack(); setLocationRelativeTo(GuiUtils.getMainFrame()); // listen to the visibility setting/ disposes of the dialog when it becomes invisible addComponentListener(new ComponentListener(){ @Override public void componentHidden(ComponentEvent e) { dispose(); } @Override public void componentMoved(ComponentEvent e) {} @Override public void componentResized(ComponentEvent e) {} @Override public void componentShown(ComponentEvent e) { if(okButton != null) okButton.requestFocusInWindow(); } }); renameListener = new RenameListener(libraryManager); libraryManager.getLibraryManagedList().addPropertyChangeListener(renameListener); tabPanel.addSearchTabListener(new FileInfoTabListener(){ @Override public void tabSelected(Tabs tab) { if(!cards.containsKey(tab)) { if(tab == Tabs.GENERAL) { cards.put(tab, fileInfoFactory.createGeneralPanel(type, propertiableFile)); } else if(tab == Tabs.SHARING) { cards.put(tab, fileInfoFactory.createSharingPanel(type, propertiableFile)); } else if(tab== Tabs.LIMITS) { Torrent torrent = (Torrent) propertiableFile.getProperty(FilePropertyKey.TORRENT); if (torrent != null && torrent.isEditable()) { cards.put(tab, fileInfoFactory.createLimitsPanel(type, propertiableFile)); } } else if (tab == Tabs.TRACKERS) { cards.put(tab, fileInfoFactory.createTrackersPanel(type, propertiableFile)); } else if(tab == Tabs.TRANSFERS) { cards.put(tab, fileInfoFactory.createTransferPanel(type, propertiableFile)); } else if(tab == Tabs.BITTORENT) { Torrent torrent = (Torrent) propertiableFile.getProperty(FilePropertyKey.TORRENT); //we can't show the file info panel for torrents without metadata. if(torrent != null && torrent.hasMetaData()) { cards.put(tab, fileInfoFactory.createBittorentPanel(torrent)); } } else if (tab == Tabs.PIECES) { if(propertiableFile instanceof DownloadItem) { cards.put(tab, fileInfoFactory.createPiecesPanel(type, (DownloadItem)propertiableFile)); } else { // TODO: Pieces should still be available in some cases for torrents.. // Need to overhaul file info. } } else { throw new IllegalStateException("Unknown state:" + tab); } } cardPanel.removeAll(); cardPanel.add(cards.get(tab).getComponent()); FileInfoDialog.this.validate(); FileInfoDialog.this.repaint(); } }); } @Override public void dispose() { overviewPanel.dispose(); //unregister any listeners used and dispose of dialog when made invisible for(FileInfoPanel panel : cards.values()) { panel.dispose(); } libraryManager.getLibraryManagedList().removePropertyChangeListener(renameListener); super.dispose(); } private void updateTitle(String fileName) { setTitle(I18n.tr("{0} Properties", propertiableFile.getFileName())); } public void renameFile() { overviewPanel.enableRename(); } private void createTabs(PropertiableFile propertiableFile, final FileInfoType type) { List<Tabs> tabs = new ArrayList<Tabs>(); // general tab is always shown tabs.add(Tabs.GENERAL); Torrent torrent = (Torrent)propertiableFile.getProperty(FilePropertyKey.TORRENT); if (torrent != null) { if (torrent.isEditable()) { tabs.add(Tabs.LIMITS); } if (torrent.hasMetaData()) { tabs.add(Tabs.BITTORENT); tabs.add(Tabs.TRACKERS); } if (torrent.isEditable()) { tabs.add(Tabs.TRANSFERS); // TODO: Pieces should still be available in some cases for torrents.. // Need to overhaul file info. if (type == FileInfoType.DOWNLOADING_FILE) { tabs.add(Tabs.PIECES); } } } else { if (type == FileInfoType.LOCAL_FILE) { tabs.add(Tabs.SHARING); } if (type != FileInfoType.REMOTE_FILE) { tabs.add(Tabs.TRANSFERS); } if (type == FileInfoType.DOWNLOADING_FILE) { tabs.add(Tabs.PIECES); } } tabPanel.setTabs(tabs); } /** * Adds a footer with the cancel/ok button to close the dialog. */ private void createFooter() { okButton = new JButton(new OKAction()); JPanel footerPanel = new JPanel(new MigLayout("fill, insets 0 15 10 15")); footerPanel.add(okButton, "alignx right, aligny bottom, split, tag ok"); footerPanel.add(new JButton(new CancelAction()), "aligny bottom, tag cancel"); footerPanel.setBackground(backgroundColor); add(footerPanel, "grow, south"); } /** * Closes the dialog and saves any data that may have changed. */ private class OKAction extends AbstractAction { public OKAction() { super(tr("OK")); } @Override public void actionPerformed(ActionEvent e) { overviewPanel.save(); for(FileInfoPanel panel : cards.values()) { panel.save(); } setVisible(false); } } /** * Closes the data and does not save any data even if it * has changed. */ private class CancelAction extends AbstractAction { public CancelAction() { super(tr("Cancel")); } @Override public void actionPerformed(ActionEvent e) { setVisible(false); } } /** * Listens for rename events. If this file was renamed, * updates all the created panels with the new * propertiable file since the underlying FileDesc * has changed. */ private class RenameListener implements PropertyChangeListener { private LibraryManager libraryManager; public RenameListener(LibraryManager libraryManager) { this.libraryManager = libraryManager; } @Override public void propertyChange(PropertyChangeEvent evt) { // if its a rename event, check if this propertiable file name changed. if(evt.getPropertyName().equals("rename")) { // if this file name changed, update the propertiable file on all the created panels so // they can properlly save if( propertiableFile instanceof LocalFileItem && ((LocalFileItem)propertiableFile).getFile().equals(evt.getOldValue())) { propertiableFile = libraryManager.getLibraryManagedList().getFileItem((File)evt.getNewValue()); for(FileInfoPanel panel : cards.values()) { panel.updatePropertiableFile(propertiableFile); } overviewPanel.updatePropertiableFile(propertiableFile); updateTitle(propertiableFile.getFileName()); } } } } }