package org.limewire.ui.swing.warnings; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Font; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.FileFilter; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Set; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JLayeredPane; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JTextField; import javax.swing.JToggleButton; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import net.miginfocom.swing.MigLayout; import org.jdesktop.application.Resource; import org.limewire.core.api.Category; import org.limewire.core.api.file.CategoryManager; import org.limewire.core.api.library.LocalFileList; import org.limewire.core.api.library.SharedFileList; import org.limewire.core.settings.LibrarySettings; import org.limewire.ui.swing.action.AbstractAction; import org.limewire.ui.swing.components.HorizonalCheckBoxListPanel; import org.limewire.ui.swing.components.HyperlinkButton; import org.limewire.ui.swing.components.Line; import org.limewire.ui.swing.components.OverlayPopupPanel; import org.limewire.ui.swing.components.PopupHeaderBar; import org.limewire.ui.swing.mainframe.GlobalLayeredPane; import org.limewire.ui.swing.options.ExtensionClassificationPanel; import org.limewire.ui.swing.settings.SwingUiSettings; import org.limewire.ui.swing.util.GuiUtils; import org.limewire.ui.swing.util.I18n; import org.limewire.util.FileUtils; import com.google.common.collect.ImmutableSortedSet; import com.google.inject.Inject; import com.google.inject.Provider; class LibraryWarningDialog extends OverlayPopupPanel { private static int HEIGHT_COLLAPSED = 185; private static int HEIGHT_OPEN = 240; private final CategoryManager categoryManager; private final LibraryFileAdder libraryFileAdder; private final Provider<ExtensionClassificationPanel> classificationPanelProvider; @Resource private Color border; @Resource private Font normalFont; @Resource private Color fontColor; @Resource private Icon upIcon; @Resource private Icon downIcon; private boolean isShared; private CancelAction cancelAction; private LocalFileList localFileList; private List<File> files; private JRadioButton recursiveButton; private JRadioButton nonRecursiveButton; private Line horizontalLine; private JToggleButton advancedToggleButton; private JTextField extensionTextField; private JLabel advancedLabel; private HorizonalCheckBoxListPanel<Category> categories; @Inject public LibraryWarningDialog( @GlobalLayeredPane JLayeredPane layeredPane, LibraryFileAdder libraryFileAdder, CategoryManager categoryManager, Provider<ExtensionClassificationPanel> classificationPanelProvider) { super(layeredPane); this.libraryFileAdder = libraryFileAdder; this.categoryManager = categoryManager; this.classificationPanelProvider = classificationPanelProvider; GuiUtils.assignResources(this); setLayout(new BorderLayout()); } public void initialize(final LocalFileList fileList, final List<File> files) { this.localFileList = fileList; this.files = files; cancelAction = new CancelAction(); //this must be called prior to any ui components being created setIsShared(fileList); PopupHeaderBar header = new PopupHeaderBar(getHeaderString(), cancelAction); add(header, BorderLayout.NORTH); add(createBody(), BorderLayout.CENTER); repaint(); validate(); } private JPanel createBody() { JPanel p = new JPanel(new MigLayout("fill, gap 0")); p.setBorder(BorderFactory.createMatteBorder(0, 1, 1, 1, border)); JLabel messageLabel = new JLabel(getMessageString(localFileList)); decorateComponent(messageLabel); HyperlinkButton learnMoreLink = new HyperlinkButton(new AbstractAction("(" + I18n.tr("Learn More") + ")"){ @Override public void actionPerformed(ActionEvent e) { classificationPanelProvider.get().showDialogue(); } }); p.add(messageLabel, "span, split 2"); p.add(learnMoreLink, "wrap"); categories = createCheckBoxes(); p.add(categories, "span, alignx center, gapbottom 5, wrap"); File folder = getFolderBeingAdded(files); int folderCount = getFolderCount(files); String recursiveText = toHTML(getRecursiveLabel(folder, folderCount)); recursiveButton = new JRadioButton(recursiveText); recursiveButton.setBorder(BorderFactory.createEmptyBorder()); String nonRecursiveText = toHTML(getNonRecursiveLabel(folder, folderCount)); nonRecursiveButton = new JRadioButton(nonRecursiveText); nonRecursiveButton.setBorder(BorderFactory.createEmptyBorder()); ButtonGroup radioButtonGroup = new ButtonGroup(); radioButtonGroup.add(recursiveButton); radioButtonGroup.add(nonRecursiveButton); recursiveButton.setSelected(LibrarySettings.DEFAULT_RECURSIVELY_ADD_FOLDERS_OPTION.getValue()); JLabel fromLabel = new JLabel(I18n.tr("From:")); decorateComponent(fromLabel); p.add(fromLabel, "span, wrap"); p.add(recursiveButton, "span, growy, wrap, gapleft 10"); p.add(nonRecursiveButton, "span, growy, wrap, gapleft 10"); horizontalLine = Line.createHorizontalLine(border, 1); horizontalLine.setVisible(false); p.add(horizontalLine, "span, growx, gapleft 10, gapright 10, gaptop 10, hidemode 3, wrap"); p.add(createAdvancedPanel(), "gaptop 5"); p.add(new JButton(new OKAction()), "split, aligny bottom, tag ok"); p.add(new JButton(cancelAction), "tag cancel, aligny bottom, wrap"); return p; } private HorizonalCheckBoxListPanel<Category> createCheckBoxes() { List<Category> categoryList = Arrays.asList(Category.AUDIO, Category.VIDEO, Category.IMAGE, Category.DOCUMENT, Category.PROGRAM); HorizonalCheckBoxListPanel<Category> categoriesPanel = new HorizonalCheckBoxListPanel<Category>(categoryList); categoriesPanel.setSelected(SwingUiSettings.getDefaultSelectedCategories()); categoriesPanel.setForeground(fontColor); categoriesPanel.setFont(normalFont); //disable program check box is programs is disabled in settings if(!LibrarySettings.ALLOW_PROGRAMS.get()) { categoriesPanel.getCheckBox(Category.PROGRAM).setEnabled(false); categoriesPanel.getCheckBox(Category.PROGRAM).setSelected(false); } //disable documents check box if documents setting is disabled and //this is the public share list. if(isShared && localFileList instanceof SharedFileList && ((SharedFileList)localFileList).isPublic() && !LibrarySettings.ALLOW_DOCUMENT_GNUTELLA_SHARING.get()) { categoriesPanel.getCheckBox(Category.DOCUMENT).setEnabled(false); categoriesPanel.getCheckBox(Category.DOCUMENT).setSelected(false); } return categoriesPanel; } /** * Creates the collapsable advanced panel for adding extensions. */ private JPanel createAdvancedPanel() { JPanel p = new JPanel(new MigLayout("fill, insets 0, gap 0, hidemode 3")); advancedToggleButton = new JToggleButton(I18n.tr("Advanced")); advancedToggleButton.setSelectedIcon(upIcon); advancedToggleButton.setIcon(downIcon); advancedToggleButton.setBorder(BorderFactory.createEmptyBorder()); advancedToggleButton.setOpaque(false); advancedToggleButton.setContentAreaFilled(false); advancedToggleButton.setFocusPainted(false); advancedToggleButton.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent e) { horizontalLine.setVisible(advancedToggleButton.isSelected()); extensionTextField.setVisible(advancedToggleButton.isSelected()); advancedLabel.setVisible(advancedToggleButton.isSelected()); LibraryWarningDialog.this.resize(); } }); advancedLabel = new JLabel(I18n.tr("Include files with the following extensions(abc, xyz)")); decorateComponent(advancedLabel); advancedLabel.setVisible(false); extensionTextField = new JTextField(); extensionTextField.getDocument().addDocumentListener(new DocumentListener(){ @Override public void changedUpdate(DocumentEvent e) {} @Override public void insertUpdate(DocumentEvent e) { if(extensionTextField.getText().trim().length() > 0) advancedToggleButton.setEnabled(false); } @Override public void removeUpdate(DocumentEvent e) { if(extensionTextField.getText().trim().length() == 0) advancedToggleButton.setEnabled(true); } }); decorateComponent(extensionTextField); extensionTextField.setVisible(false); p.add(advancedToggleButton, "span, wrap, aligny bottom"); p.add(advancedLabel, "span, gaptop 5, wrap"); p.add(extensionTextField, "gaptop 5, growx"); return p; } private void decorateComponent(JComponent component) { component.setFont(normalFont); component.setForeground(fontColor); } private String toHTML(String text){ return "<HTML><HEAD>" + createFontCSS(normalFont) + "</HEAD>" + text + "</HTML>"; } private String createFontCSS(Font font) { return "<style TYPE=\"text/css\"><!--" + "body {font-family: " + font.getFamily() + ";" + "font-size: " + font.getSize() + "pt;" + "font-weight: lighter;}-->" + "</style>"; } /** * Sets the boolean value for whether the list the files being added * to is shared or not. */ private void setIsShared(LocalFileList fileList) { if(fileList instanceof SharedFileList && ( ((SharedFileList)fileList).isPublic() || ((SharedFileList)fileList).getFriendIds().size() > 0)) { isShared = true; } else { isShared = false; } } /** * Returns the String to display in the header. */ private String getHeaderString() { if(isShared) return I18n.tr("Choose What to Share"); else return I18n.tr("Choose What to Add"); } /** * Returns the main Message in the panel. */ private String getMessageString(LocalFileList fileList) { if(isShared) { if(fileList instanceof SharedFileList && ((SharedFileList)fileList).isPublic()) { return I18n.tr("Choose the type of files to share with the world"); } else { return I18n.trn("Choose the types of files to share with {0} friend", "Choose the types of files to share with {0} friends", ((SharedFileList)fileList).getFriendIds().size()); } } else { return I18n.tr("Add these types of files"); } } /** * If and only if one folder was added in the drop, that * file will be returned, otherwise returns null. */ private File getFolderBeingAdded(List<File> files) { int directoryCount = 0; File folder = null; for (File file : files) { if (file.isDirectory()) { directoryCount++; folder = file; if (directoryCount > 1) { // short circuit just need to know if there is more than 1 // null folder when more than 1 folder. folder = null; break; } } } return folder; } private int getFolderCount(List<File> files) { int folderCount = 0; for(File file : files) { if(file.isDirectory()) folderCount += 1; } return folderCount; } /** * Returns the text for the recursive radio button. */ private String getRecursiveLabel(File folder, int folderCount) { if(folder != null) return I18n.tr("\"{0}\" and all its subfolders", folder.getName()); else return I18n.tr("{0} folders and all their subfolders", folderCount); } /** * Returns the text for the non-recursive radio button. */ private String getNonRecursiveLabel(File folder, int folderCount) { if(folder != null) return I18n.tr("\"{0}\" only", folder.getName()); else return I18n.tr("{0} folders only", folderCount); } @Override public void resize() { Rectangle parentBounds = getParent().getBounds(); int w = 520; int h = HEIGHT_COLLAPSED; if(advancedToggleButton != null && advancedToggleButton.isSelected()) { h = HEIGHT_OPEN; } else { h = HEIGHT_COLLAPSED; } setBounds((int)parentBounds.getWidth()/2-w/2, (int)parentBounds.getHeight()/2-h/2, w, h); } /** * Returns all extensions separated by spaces or comma separated. * It ignores any preceding "." to an extension. */ private List<String> getAdvancedExtensions() { String text = extensionTextField.getText(); String[] parsed = text.split(",\\s*\\.?|\\s*\\.|\\s+"); return Arrays.asList(parsed); } /** * Accepts the drop. */ private class OKAction extends AbstractAction { public OKAction() { if(isShared) { putValue(Action.NAME, I18n.tr("Share")); } else { putValue(Action.NAME, I18n.tr("Add")); } } @Override public void actionPerformed(ActionEvent e) { List<String> includedExtensions = getAdvancedExtensions(); libraryFileAdder.addFilesInner(localFileList, files, new Filter(categories.getSelected(), includedExtensions, recursiveButton.isSelected())); LibraryWarningDialog.this.dispose(); } } /** * Disposes of the dialog and cancels the drop. */ private class CancelAction extends AbstractAction { public CancelAction() { super(I18n.tr("Cancel")); } @Override public void actionPerformed(ActionEvent e) { LibraryWarningDialog.this.dispose(); } } /** * Filter which accepts a file if its a folder or the extension matches one of the * selected categories or is added to the advanced textfield. */ private class Filter implements FileFilter { private final Set<String> extensionSet; private final boolean isRecursive; /** Constructs the filter with the given set of allowed extensions. */ public Filter(Collection<Category> categories, List<String> advancedExtensions, boolean isRecursive) { this.isRecursive = isRecursive; ImmutableSortedSet.Builder<String> builder = ImmutableSortedSet.orderedBy(String.CASE_INSENSITIVE_ORDER); builder.addAll(advancedExtensions); for(Category category : categories) { builder.addAll(categoryManager.getExtensionsForCategory(category)); } extensionSet = builder.build(); } /** * Return true if the file is a directory and recursion is allowed or * the file is not a directory and the extension is allowed. */ @Override public boolean accept(File file) { if(file.isDirectory()) { return isRecursive; } else { return extensionSet.contains(FileUtils.getFileExtension(file)); } } } }