1 /* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 package proguard.gui; 22 23 import proguard.*; 24 import proguard.util.ListUtil; 25 26 import javax.swing.*; 27 import java.awt.*; 28 import java.awt.event.*; 29 import java.io.File; 30 import java.util.List; 31 32 /** 33 * This <code>ListPanel</code> allows the user to add, edit, filter, move, and 34 * remove ClassPathEntry objects in a ClassPath object. 35 * 36 * @author Eric Lafortune 37 */ 38 class ClassPathPanel extends ListPanel 39 { 40 private final JFrame owner; 41 private final boolean inputAndOutput; 42 private final JFileChooser chooser; 43 private final FilterDialog filterDialog; 44 45 ClassPathPanel(JFrame owner, boolean inputAndOutput)46 public ClassPathPanel(JFrame owner, boolean inputAndOutput) 47 { 48 super(); 49 50 super.firstSelectionButton = inputAndOutput ? 3 : 2; 51 52 this.owner = owner; 53 this.inputAndOutput = inputAndOutput; 54 55 list.setCellRenderer(new MyListCellRenderer()); 56 57 chooser = new JFileChooser(""); 58 chooser.setMultiSelectionEnabled(true); 59 chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); 60 chooser.addChoosableFileFilter( 61 new ExtensionFileFilter(msg("jarExtensions"), 62 new String[] { ".apk", ".ap_", ".jar", ".aar", ".war", ".ear", ".zip" })); 63 chooser.setApproveButtonText(msg("ok")); 64 65 filterDialog = new FilterDialog(owner, msg("enterFilter")); 66 67 addAddButton(inputAndOutput, false); 68 if (inputAndOutput) 69 { 70 addAddButton(inputAndOutput, true); 71 } 72 addEditButton(); 73 addFilterButton(); 74 addRemoveButton(); 75 addUpButton(); 76 addDownButton(); 77 78 enableSelectionButtons(); 79 } 80 81 addAddButton(boolean inputAndOutput, final boolean isOutput)82 protected void addAddButton(boolean inputAndOutput, 83 final boolean isOutput) 84 { 85 JButton addButton = new JButton(msg(inputAndOutput ? 86 isOutput ? "addOutput" : 87 "addInput" : 88 "add")); 89 addButton.addActionListener(new ActionListener() 90 { 91 public void actionPerformed(ActionEvent e) 92 { 93 chooser.setDialogTitle(msg("addJars")); 94 chooser.setSelectedFile(null); 95 chooser.setSelectedFiles(null); 96 97 int returnValue = chooser.showOpenDialog(owner); 98 if (returnValue == JFileChooser.APPROVE_OPTION) 99 { 100 File[] selectedFiles = chooser.getSelectedFiles(); 101 ClassPathEntry[] entries = classPathEntries(selectedFiles, isOutput); 102 103 // Add the new elements. 104 addElements(entries); 105 } 106 } 107 }); 108 109 addButton(tip(addButton, inputAndOutput ? 110 isOutput ? "addOutputTip" : 111 "addInputTip" : 112 "addTip")); 113 } 114 115 addEditButton()116 protected void addEditButton() 117 { 118 JButton editButton = new JButton(msg("edit")); 119 editButton.addActionListener(new ActionListener() 120 { 121 public void actionPerformed(ActionEvent e) 122 { 123 boolean isOutput = false; 124 125 int[] selectedIndices = list.getSelectedIndices(); 126 127 // Copy the Object array into a File array. 128 File[] selectedFiles = new File[selectedIndices.length]; 129 for (int index = 0; index < selectedFiles.length; index++) 130 { 131 ClassPathEntry entry = 132 (ClassPathEntry)listModel.getElementAt(selectedIndices[index]); 133 134 isOutput = entry.isOutput(); 135 136 selectedFiles[index] = entry.getFile(); 137 } 138 139 chooser.setDialogTitle(msg("chooseJars")); 140 141 // Up to JDK 1.3.1, setSelectedFiles doesn't show in the file 142 // chooser, so we just use setSelectedFile first. It also sets 143 // the current directory. 144 chooser.setSelectedFile(selectedFiles[0].getAbsoluteFile()); 145 chooser.setSelectedFiles(selectedFiles); 146 147 int returnValue = chooser.showOpenDialog(owner); 148 if (returnValue == JFileChooser.APPROVE_OPTION) 149 { 150 selectedFiles = chooser.getSelectedFiles(); 151 ClassPathEntry[] entries = classPathEntries(selectedFiles, isOutput); 152 153 // If there are the same number of files selected now as 154 // there were before, we can just replace the old ones. 155 if (selectedIndices.length == selectedFiles.length) 156 { 157 // Replace the old elements. 158 setElementsAt(entries, selectedIndices); 159 } 160 else 161 { 162 // Remove the old elements. 163 removeElementsAt(selectedIndices); 164 165 // Add the new elements. 166 addElements(entries); 167 } 168 } 169 } 170 }); 171 172 addButton(tip(editButton, "editTip")); 173 } 174 175 addFilterButton()176 protected void addFilterButton() 177 { 178 JButton filterButton = new JButton(msg("filter")); 179 filterButton.addActionListener(new ActionListener() 180 { 181 public void actionPerformed(ActionEvent e) 182 { 183 if (!list.isSelectionEmpty()) 184 { 185 int[] selectedIndices = list.getSelectedIndices(); 186 187 // Put the filters of the first selected entry in the dialog. 188 getFiltersFrom(selectedIndices[0]); 189 190 int returnValue = filterDialog.showDialog(); 191 if (returnValue == FilterDialog.APPROVE_OPTION) 192 { 193 // Apply the entered filters to all selected entries. 194 setFiltersAt(selectedIndices); 195 } 196 } 197 } 198 }); 199 200 addButton(tip(filterButton, "filterTip")); 201 } 202 203 204 /** 205 * Sets the ClassPath to be represented in this panel. 206 */ setClassPath(ClassPath classPath)207 public void setClassPath(ClassPath classPath) 208 { 209 listModel.clear(); 210 211 if (classPath != null) 212 { 213 for (int index = 0; index < classPath.size(); index++) 214 { 215 listModel.addElement(classPath.get(index)); 216 } 217 } 218 219 // Make sure the selection buttons are properly enabled, 220 // since the clear method doesn't seem to notify the listener. 221 enableSelectionButtons(); 222 } 223 224 225 /** 226 * Returns the ClassPath currently represented in this panel. 227 */ getClassPath()228 public ClassPath getClassPath() 229 { 230 int size = listModel.size(); 231 if (size == 0) 232 { 233 return null; 234 } 235 236 ClassPath classPath = new ClassPath(); 237 for (int index = 0; index < size; index++) 238 { 239 classPath.add((ClassPathEntry)listModel.get(index)); 240 } 241 242 return classPath; 243 } 244 245 246 /** 247 * Converts the given array of File objects into a corresponding array of 248 * ClassPathEntry objects. 249 */ classPathEntries(File[] files, boolean isOutput)250 private ClassPathEntry[] classPathEntries(File[] files, boolean isOutput) 251 { 252 ClassPathEntry[] entries = new ClassPathEntry[files.length]; 253 for (int index = 0; index < entries.length; index++) 254 { 255 entries[index] = new ClassPathEntry(files[index], isOutput); 256 } 257 return entries; 258 } 259 260 261 /** 262 * Sets up the filter dialog with the filters from the specified class path 263 * entry. 264 */ getFiltersFrom(int index)265 private void getFiltersFrom(int index) 266 { 267 ClassPathEntry firstEntry = (ClassPathEntry)listModel.get(index); 268 269 filterDialog.setFilter(firstEntry.getFilter()); 270 filterDialog.setApkFilter(firstEntry.getApkFilter()); 271 filterDialog.setJarFilter(firstEntry.getJarFilter()); 272 filterDialog.setAarFilter(firstEntry.getAarFilter()); 273 filterDialog.setWarFilter(firstEntry.getWarFilter()); 274 filterDialog.setEarFilter(firstEntry.getEarFilter()); 275 filterDialog.setZipFilter(firstEntry.getZipFilter()); 276 } 277 278 279 /** 280 * Applies the entered filter to the specified class path entries. 281 * Any previously set filters are discarded. 282 */ setFiltersAt(int[] indices)283 private void setFiltersAt(int[] indices) 284 { 285 for (int index = indices.length - 1; index >= 0; index--) 286 { 287 ClassPathEntry entry = (ClassPathEntry)listModel.get(indices[index]); 288 entry.setFilter(filterDialog.getFilter()); 289 entry.setApkFilter(filterDialog.getApkFilter()); 290 entry.setJarFilter(filterDialog.getJarFilter()); 291 entry.setAarFilter(filterDialog.getAarFilter()); 292 entry.setWarFilter(filterDialog.getWarFilter()); 293 entry.setEarFilter(filterDialog.getEarFilter()); 294 entry.setZipFilter(filterDialog.getZipFilter()); 295 } 296 297 // Make sure they are selected and thus repainted. 298 list.setSelectedIndices(indices); 299 } 300 301 302 /** 303 * Attaches the tool tip from the GUI resources that corresponds to the 304 * given key, to the given component. 305 */ tip(JComponent component, String messageKey)306 private static JComponent tip(JComponent component, String messageKey) 307 { 308 component.setToolTipText(msg(messageKey)); 309 310 return component; 311 } 312 313 314 /** 315 * Returns the message from the GUI resources that corresponds to the given 316 * key. 317 */ msg(String messageKey)318 private static String msg(String messageKey) 319 { 320 return GUIResources.getMessage(messageKey); 321 } 322 323 324 /** 325 * This ListCellRenderer renders ClassPathEntry objects. 326 */ 327 private class MyListCellRenderer implements ListCellRenderer 328 { 329 private static final String ARROW_IMAGE_FILE = "arrow.gif"; 330 331 private final JPanel cellPanel = new JPanel(new GridBagLayout()); 332 private final JLabel iconLabel = new JLabel("", JLabel.RIGHT); 333 private final JLabel jarNameLabel = new JLabel("", JLabel.RIGHT); 334 private final JLabel filterLabel = new JLabel("", JLabel.RIGHT); 335 336 private final Icon arrowIcon; 337 338 MyListCellRenderer()339 public MyListCellRenderer() 340 { 341 GridBagConstraints jarNameLabelConstraints = new GridBagConstraints(); 342 jarNameLabelConstraints.anchor = GridBagConstraints.WEST; 343 jarNameLabelConstraints.insets = new Insets(1, 2, 1, 2); 344 345 GridBagConstraints filterLabelConstraints = new GridBagConstraints(); 346 filterLabelConstraints.gridwidth = GridBagConstraints.REMAINDER; 347 filterLabelConstraints.fill = GridBagConstraints.HORIZONTAL; 348 filterLabelConstraints.weightx = 1.0; 349 filterLabelConstraints.anchor = GridBagConstraints.EAST; 350 filterLabelConstraints.insets = jarNameLabelConstraints.insets; 351 352 arrowIcon = new ImageIcon(Toolkit.getDefaultToolkit().getImage(this.getClass().getResource(ARROW_IMAGE_FILE))); 353 354 cellPanel.add(iconLabel, jarNameLabelConstraints); 355 cellPanel.add(jarNameLabel, jarNameLabelConstraints); 356 cellPanel.add(filterLabel, filterLabelConstraints); 357 } 358 359 360 // Implementations for ListCellRenderer. 361 getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)362 public Component getListCellRendererComponent(JList list, 363 Object value, 364 int index, 365 boolean isSelected, 366 boolean cellHasFocus) 367 { 368 ClassPathEntry entry = (ClassPathEntry)value; 369 370 // Prepend an arrow to the output entries. 371 if (inputAndOutput && entry.isOutput()) 372 { 373 iconLabel.setIcon(arrowIcon); 374 } 375 else 376 { 377 iconLabel.setIcon(null); 378 } 379 380 // Set the entry name text. 381 jarNameLabel.setText(entry.getName()); 382 383 // Set the filter text. 384 StringBuffer filter = null; 385 filter = appendFilter(filter, entry.getZipFilter()); 386 filter = appendFilter(filter, entry.getEarFilter()); 387 filter = appendFilter(filter, entry.getWarFilter()); 388 filter = appendFilter(filter, entry.getAarFilter()); 389 filter = appendFilter(filter, entry.getJarFilter()); 390 filter = appendFilter(filter, entry.getApkFilter()); 391 filter = appendFilter(filter, entry.getFilter()); 392 393 if (filter != null) 394 { 395 filter.append(')'); 396 } 397 398 filterLabel.setText(filter != null ? filter.toString() : ""); 399 400 // Set the colors. 401 if (isSelected) 402 { 403 cellPanel.setBackground(list.getSelectionBackground()); 404 jarNameLabel.setForeground(list.getSelectionForeground()); 405 filterLabel.setForeground(list.getSelectionForeground()); 406 } 407 else 408 { 409 cellPanel.setBackground(list.getBackground()); 410 jarNameLabel.setForeground(list.getForeground()); 411 filterLabel.setForeground(list.getForeground()); 412 } 413 414 // Make the font color red if this is an input file that can't be read. 415 if (!(inputAndOutput && entry.isOutput()) && 416 !entry.getFile().canRead()) 417 { 418 jarNameLabel.setForeground(Color.red); 419 } 420 421 cellPanel.setOpaque(true); 422 423 return cellPanel; 424 } 425 426 appendFilter(StringBuffer filter, List additionalFilter)427 private StringBuffer appendFilter(StringBuffer filter, List additionalFilter) 428 { 429 if (filter != null) 430 { 431 filter.append(';'); 432 } 433 434 if (additionalFilter != null) 435 { 436 if (filter == null) 437 { 438 filter = new StringBuffer().append('('); 439 } 440 441 filter.append(ListUtil.commaSeparatedString(additionalFilter, true)); 442 } 443 444 return filter; 445 } 446 } 447 } 448