/*
|
* ProGuard -- shrinking, optimization, obfuscation, and preverification
|
* of Java bytecode.
|
*
|
* Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu)
|
*
|
* This program is free software; you can redistribute it and/or modify it
|
* under the terms of the GNU General Public License as published by the Free
|
* Software Foundation; either version 2 of the License, or (at your option)
|
* any later version.
|
*
|
* 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 General Public License for
|
* more details.
|
*
|
* You should have received a copy of the GNU General Public License along
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
*/
|
package proguard.gui;
|
|
import proguard.*;
|
import proguard.util.ListUtil;
|
|
import javax.swing.*;
|
import java.awt.*;
|
import java.awt.event.*;
|
import java.io.File;
|
import java.util.List;
|
|
/**
|
* This <code>ListPanel</code> allows the user to add, edit, filter, move, and
|
* remove ClassPathEntry objects in a ClassPath object.
|
*
|
* @author Eric Lafortune
|
*/
|
class ClassPathPanel extends ListPanel
|
{
|
private final JFrame owner;
|
private final boolean inputAndOutput;
|
private final JFileChooser chooser;
|
private final FilterDialog filterDialog;
|
|
|
public ClassPathPanel(JFrame owner, boolean inputAndOutput)
|
{
|
super();
|
|
super.firstSelectionButton = inputAndOutput ? 3 : 2;
|
|
this.owner = owner;
|
this.inputAndOutput = inputAndOutput;
|
|
list.setCellRenderer(new MyListCellRenderer());
|
|
chooser = new JFileChooser("");
|
chooser.setMultiSelectionEnabled(true);
|
chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
|
chooser.addChoosableFileFilter(
|
new ExtensionFileFilter(msg("jarExtensions"),
|
new String[] { ".apk", ".ap_", ".jar", ".aar", ".war", ".ear", ".zip" }));
|
chooser.setApproveButtonText(msg("ok"));
|
|
filterDialog = new FilterDialog(owner, msg("enterFilter"));
|
|
addAddButton(inputAndOutput, false);
|
if (inputAndOutput)
|
{
|
addAddButton(inputAndOutput, true);
|
}
|
addEditButton();
|
addFilterButton();
|
addRemoveButton();
|
addUpButton();
|
addDownButton();
|
|
enableSelectionButtons();
|
}
|
|
|
protected void addAddButton(boolean inputAndOutput,
|
final boolean isOutput)
|
{
|
JButton addButton = new JButton(msg(inputAndOutput ?
|
isOutput ? "addOutput" :
|
"addInput" :
|
"add"));
|
addButton.addActionListener(new ActionListener()
|
{
|
public void actionPerformed(ActionEvent e)
|
{
|
chooser.setDialogTitle(msg("addJars"));
|
chooser.setSelectedFile(null);
|
chooser.setSelectedFiles(null);
|
|
int returnValue = chooser.showOpenDialog(owner);
|
if (returnValue == JFileChooser.APPROVE_OPTION)
|
{
|
File[] selectedFiles = chooser.getSelectedFiles();
|
ClassPathEntry[] entries = classPathEntries(selectedFiles, isOutput);
|
|
// Add the new elements.
|
addElements(entries);
|
}
|
}
|
});
|
|
addButton(tip(addButton, inputAndOutput ?
|
isOutput ? "addOutputTip" :
|
"addInputTip" :
|
"addTip"));
|
}
|
|
|
protected void addEditButton()
|
{
|
JButton editButton = new JButton(msg("edit"));
|
editButton.addActionListener(new ActionListener()
|
{
|
public void actionPerformed(ActionEvent e)
|
{
|
boolean isOutput = false;
|
|
int[] selectedIndices = list.getSelectedIndices();
|
|
// Copy the Object array into a File array.
|
File[] selectedFiles = new File[selectedIndices.length];
|
for (int index = 0; index < selectedFiles.length; index++)
|
{
|
ClassPathEntry entry =
|
(ClassPathEntry)listModel.getElementAt(selectedIndices[index]);
|
|
isOutput = entry.isOutput();
|
|
selectedFiles[index] = entry.getFile();
|
}
|
|
chooser.setDialogTitle(msg("chooseJars"));
|
|
// Up to JDK 1.3.1, setSelectedFiles doesn't show in the file
|
// chooser, so we just use setSelectedFile first. It also sets
|
// the current directory.
|
chooser.setSelectedFile(selectedFiles[0].getAbsoluteFile());
|
chooser.setSelectedFiles(selectedFiles);
|
|
int returnValue = chooser.showOpenDialog(owner);
|
if (returnValue == JFileChooser.APPROVE_OPTION)
|
{
|
selectedFiles = chooser.getSelectedFiles();
|
ClassPathEntry[] entries = classPathEntries(selectedFiles, isOutput);
|
|
// If there are the same number of files selected now as
|
// there were before, we can just replace the old ones.
|
if (selectedIndices.length == selectedFiles.length)
|
{
|
// Replace the old elements.
|
setElementsAt(entries, selectedIndices);
|
}
|
else
|
{
|
// Remove the old elements.
|
removeElementsAt(selectedIndices);
|
|
// Add the new elements.
|
addElements(entries);
|
}
|
}
|
}
|
});
|
|
addButton(tip(editButton, "editTip"));
|
}
|
|
|
protected void addFilterButton()
|
{
|
JButton filterButton = new JButton(msg("filter"));
|
filterButton.addActionListener(new ActionListener()
|
{
|
public void actionPerformed(ActionEvent e)
|
{
|
if (!list.isSelectionEmpty())
|
{
|
int[] selectedIndices = list.getSelectedIndices();
|
|
// Put the filters of the first selected entry in the dialog.
|
getFiltersFrom(selectedIndices[0]);
|
|
int returnValue = filterDialog.showDialog();
|
if (returnValue == FilterDialog.APPROVE_OPTION)
|
{
|
// Apply the entered filters to all selected entries.
|
setFiltersAt(selectedIndices);
|
}
|
}
|
}
|
});
|
|
addButton(tip(filterButton, "filterTip"));
|
}
|
|
|
/**
|
* Sets the ClassPath to be represented in this panel.
|
*/
|
public void setClassPath(ClassPath classPath)
|
{
|
listModel.clear();
|
|
if (classPath != null)
|
{
|
for (int index = 0; index < classPath.size(); index++)
|
{
|
listModel.addElement(classPath.get(index));
|
}
|
}
|
|
// Make sure the selection buttons are properly enabled,
|
// since the clear method doesn't seem to notify the listener.
|
enableSelectionButtons();
|
}
|
|
|
/**
|
* Returns the ClassPath currently represented in this panel.
|
*/
|
public ClassPath getClassPath()
|
{
|
int size = listModel.size();
|
if (size == 0)
|
{
|
return null;
|
}
|
|
ClassPath classPath = new ClassPath();
|
for (int index = 0; index < size; index++)
|
{
|
classPath.add((ClassPathEntry)listModel.get(index));
|
}
|
|
return classPath;
|
}
|
|
|
/**
|
* Converts the given array of File objects into a corresponding array of
|
* ClassPathEntry objects.
|
*/
|
private ClassPathEntry[] classPathEntries(File[] files, boolean isOutput)
|
{
|
ClassPathEntry[] entries = new ClassPathEntry[files.length];
|
for (int index = 0; index < entries.length; index++)
|
{
|
entries[index] = new ClassPathEntry(files[index], isOutput);
|
}
|
return entries;
|
}
|
|
|
/**
|
* Sets up the filter dialog with the filters from the specified class path
|
* entry.
|
*/
|
private void getFiltersFrom(int index)
|
{
|
ClassPathEntry firstEntry = (ClassPathEntry)listModel.get(index);
|
|
filterDialog.setFilter(firstEntry.getFilter());
|
filterDialog.setApkFilter(firstEntry.getApkFilter());
|
filterDialog.setJarFilter(firstEntry.getJarFilter());
|
filterDialog.setAarFilter(firstEntry.getAarFilter());
|
filterDialog.setWarFilter(firstEntry.getWarFilter());
|
filterDialog.setEarFilter(firstEntry.getEarFilter());
|
filterDialog.setZipFilter(firstEntry.getZipFilter());
|
}
|
|
|
/**
|
* Applies the entered filter to the specified class path entries.
|
* Any previously set filters are discarded.
|
*/
|
private void setFiltersAt(int[] indices)
|
{
|
for (int index = indices.length - 1; index >= 0; index--)
|
{
|
ClassPathEntry entry = (ClassPathEntry)listModel.get(indices[index]);
|
entry.setFilter(filterDialog.getFilter());
|
entry.setApkFilter(filterDialog.getApkFilter());
|
entry.setJarFilter(filterDialog.getJarFilter());
|
entry.setAarFilter(filterDialog.getAarFilter());
|
entry.setWarFilter(filterDialog.getWarFilter());
|
entry.setEarFilter(filterDialog.getEarFilter());
|
entry.setZipFilter(filterDialog.getZipFilter());
|
}
|
|
// Make sure they are selected and thus repainted.
|
list.setSelectedIndices(indices);
|
}
|
|
|
/**
|
* Attaches the tool tip from the GUI resources that corresponds to the
|
* given key, to the given component.
|
*/
|
private static JComponent tip(JComponent component, String messageKey)
|
{
|
component.setToolTipText(msg(messageKey));
|
|
return component;
|
}
|
|
|
/**
|
* Returns the message from the GUI resources that corresponds to the given
|
* key.
|
*/
|
private static String msg(String messageKey)
|
{
|
return GUIResources.getMessage(messageKey);
|
}
|
|
|
/**
|
* This ListCellRenderer renders ClassPathEntry objects.
|
*/
|
private class MyListCellRenderer implements ListCellRenderer
|
{
|
private static final String ARROW_IMAGE_FILE = "arrow.gif";
|
|
private final JPanel cellPanel = new JPanel(new GridBagLayout());
|
private final JLabel iconLabel = new JLabel("", JLabel.RIGHT);
|
private final JLabel jarNameLabel = new JLabel("", JLabel.RIGHT);
|
private final JLabel filterLabel = new JLabel("", JLabel.RIGHT);
|
|
private final Icon arrowIcon;
|
|
|
public MyListCellRenderer()
|
{
|
GridBagConstraints jarNameLabelConstraints = new GridBagConstraints();
|
jarNameLabelConstraints.anchor = GridBagConstraints.WEST;
|
jarNameLabelConstraints.insets = new Insets(1, 2, 1, 2);
|
|
GridBagConstraints filterLabelConstraints = new GridBagConstraints();
|
filterLabelConstraints.gridwidth = GridBagConstraints.REMAINDER;
|
filterLabelConstraints.fill = GridBagConstraints.HORIZONTAL;
|
filterLabelConstraints.weightx = 1.0;
|
filterLabelConstraints.anchor = GridBagConstraints.EAST;
|
filterLabelConstraints.insets = jarNameLabelConstraints.insets;
|
|
arrowIcon = new ImageIcon(Toolkit.getDefaultToolkit().getImage(this.getClass().getResource(ARROW_IMAGE_FILE)));
|
|
cellPanel.add(iconLabel, jarNameLabelConstraints);
|
cellPanel.add(jarNameLabel, jarNameLabelConstraints);
|
cellPanel.add(filterLabel, filterLabelConstraints);
|
}
|
|
|
// Implementations for ListCellRenderer.
|
|
public Component getListCellRendererComponent(JList list,
|
Object value,
|
int index,
|
boolean isSelected,
|
boolean cellHasFocus)
|
{
|
ClassPathEntry entry = (ClassPathEntry)value;
|
|
// Prepend an arrow to the output entries.
|
if (inputAndOutput && entry.isOutput())
|
{
|
iconLabel.setIcon(arrowIcon);
|
}
|
else
|
{
|
iconLabel.setIcon(null);
|
}
|
|
// Set the entry name text.
|
jarNameLabel.setText(entry.getName());
|
|
// Set the filter text.
|
StringBuffer filter = null;
|
filter = appendFilter(filter, entry.getZipFilter());
|
filter = appendFilter(filter, entry.getEarFilter());
|
filter = appendFilter(filter, entry.getWarFilter());
|
filter = appendFilter(filter, entry.getAarFilter());
|
filter = appendFilter(filter, entry.getJarFilter());
|
filter = appendFilter(filter, entry.getApkFilter());
|
filter = appendFilter(filter, entry.getFilter());
|
|
if (filter != null)
|
{
|
filter.append(')');
|
}
|
|
filterLabel.setText(filter != null ? filter.toString() : "");
|
|
// Set the colors.
|
if (isSelected)
|
{
|
cellPanel.setBackground(list.getSelectionBackground());
|
jarNameLabel.setForeground(list.getSelectionForeground());
|
filterLabel.setForeground(list.getSelectionForeground());
|
}
|
else
|
{
|
cellPanel.setBackground(list.getBackground());
|
jarNameLabel.setForeground(list.getForeground());
|
filterLabel.setForeground(list.getForeground());
|
}
|
|
// Make the font color red if this is an input file that can't be read.
|
if (!(inputAndOutput && entry.isOutput()) &&
|
!entry.getFile().canRead())
|
{
|
jarNameLabel.setForeground(Color.red);
|
}
|
|
cellPanel.setOpaque(true);
|
|
return cellPanel;
|
}
|
|
|
private StringBuffer appendFilter(StringBuffer filter, List additionalFilter)
|
{
|
if (filter != null)
|
{
|
filter.append(';');
|
}
|
|
if (additionalFilter != null)
|
{
|
if (filter == null)
|
{
|
filter = new StringBuffer().append('(');
|
}
|
|
filter.append(ListUtil.commaSeparatedString(additionalFilter, true));
|
}
|
|
return filter;
|
}
|
}
|
}
|