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 */ 21package proguard.gui; 22 23import proguard.*; 24import proguard.util.ListUtil; 25 26import javax.swing.*; 27import java.awt.*; 28import java.awt.event.*; 29import java.io.File; 30import 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 */ 38class 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 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 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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