diff options
Diffstat (limited to 'netx/net/sourceforge/jnlp/controlpanel')
-rw-r--r-- | netx/net/sourceforge/jnlp/controlpanel/CachePane.java | 328 | ||||
-rw-r--r-- | netx/net/sourceforge/jnlp/controlpanel/CacheViewer.java | 39 |
2 files changed, 262 insertions, 105 deletions
diff --git a/netx/net/sourceforge/jnlp/controlpanel/CachePane.java b/netx/net/sourceforge/jnlp/controlpanel/CachePane.java index 502cf8a..d5bb267 100644 --- a/netx/net/sourceforge/jnlp/controlpanel/CachePane.java +++ b/netx/net/sourceforge/jnlp/controlpanel/CachePane.java @@ -1,5 +1,5 @@ /* CachePane.java -- Displays the specified folder and allows modification to its content. -Copyright (C) 2010 Red Hat +Copyright (C) 2013 Red Hat 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 @@ -19,22 +19,27 @@ package net.sourceforge.jnlp.controlpanel; import java.awt.BorderLayout; import java.awt.Component; +import java.awt.Cursor; import java.awt.Dimension; +import java.awt.EventQueue; import java.awt.FlowLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; +import java.awt.SystemColor; +import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.channels.FileLock; import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; +import java.text.NumberFormat; import java.util.ArrayList; import java.util.Comparator; +import java.util.Date; import java.util.Enumeration; import java.util.List; @@ -45,7 +50,10 @@ import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.ListSelectionModel; -import javax.swing.table.DefaultTableModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableModel; import javax.swing.table.TableRowSorter; import net.sourceforge.jnlp.cache.CacheDirectory; @@ -54,23 +62,25 @@ import net.sourceforge.jnlp.cache.DirectoryNode; import net.sourceforge.jnlp.config.DeploymentConfiguration; import net.sourceforge.jnlp.runtime.Translator; import net.sourceforge.jnlp.util.FileUtils; -import net.sourceforge.jnlp.util.logging.OutputController; import net.sourceforge.jnlp.util.PropertiesFile; +import net.sourceforge.jnlp.util.logging.OutputController; +import net.sourceforge.jnlp.util.ui.NonEditableTableModel; public class CachePane extends JPanel { - JDialog parent; DeploymentConfiguration config; private String location; private JComponent defaultFocusComponent; DirectoryNode root; - String[] columns = { Translator.R("CVCPColName"), + String[] columns = { + Translator.R("CVCPColName"), Translator.R("CVCPColPath"), Translator.R("CVCPColType"), Translator.R("CVCPColDomain"), Translator.R("CVCPColSize"), Translator.R("CVCPColLastModified") }; JTable cacheTable; + private JButton deleteButton, refreshButton, doneButton; /** * Creates a new instance of the CachePane. @@ -95,42 +105,64 @@ public class CachePane extends JPanel { GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; - DefaultTableModel model = new DefaultTableModel(columns, 0) { - public boolean isCellEditable(int row, int column) { - return false; - } - }; + TableModel model = new NonEditableTableModel(columns, 0); cacheTable = new JTable(model); cacheTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + cacheTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + final public void valueChanged(ListSelectionEvent listSelectionEvent) { + // If no row has been selected, disable the delete button, else enable it + if (cacheTable.getSelectionModel().isSelectionEmpty()) + // Disable delete button, since nothing selected + deleteButton.setEnabled(false); + else + // Enable delete button, since something selected + deleteButton.setEnabled(true); + } + }); cacheTable.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN); cacheTable.setPreferredScrollableViewportSize(new Dimension(600, 200)); cacheTable.setFillsViewportHeight(true); JScrollPane scrollPane = new JScrollPane(cacheTable); - populateTable(); - - TableRowSorter<DefaultTableModel> tableSorter = new TableRowSorter<DefaultTableModel>(model); - tableSorter.setComparator(4, new Comparator<Long>() { // Comparator for size column. - @Override - public int compare(Long o1, Long o2) { - return o1.compareTo(o2); + TableRowSorter<TableModel> tableSorter = new TableRowSorter<TableModel>(model); + final Comparator comparator = new Comparator<Comparable>() { // General purpose Comparator + public final int compare(final Comparable a, final Comparable b) { + return a.compareTo(b); } - }); - tableSorter.setComparator(5, new Comparator<String>() { // Comparator for date column. + }; + tableSorter.setComparator(1, comparator); // Comparator for path column. + tableSorter.setComparator(4, comparator); // Comparator for size column. + tableSorter.setComparator(5, comparator); // Comparator for modified column. + cacheTable.setRowSorter(tableSorter); + final DefaultTableCellRenderer tableCellRenderer = new DefaultTableCellRenderer() { @Override - public int compare(String o1, String o2) { - DateFormat format = new SimpleDateFormat("MM/dd/yyyy"); - try { - Long time1 = format.parse(o1).getTime(); - Long time2 = format.parse(o2).getTime(); - return time1.compareTo(time2); - } catch (ParseException e) { - return 0; + public final Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) { + super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + + switch (column) { + case 1: // Path column + // Render absolute path + super.setText(((File)value).getAbsolutePath()); + break; + case 4: // Size column + // Render size formatted to default locale's number format + super.setText(NumberFormat.getInstance().format(value)); + break; + case 5: // last modified column + // Render modify date formatted to default locale's date format + super.setText(DateFormat.getDateInstance().format(value)); } + + return this; } - }); - cacheTable.setRowSorter(tableSorter); + }; + // TableCellRenderer for path column + cacheTable.getColumn(this.columns[1]).setCellRenderer(tableCellRenderer); + // TableCellRenderer for size column + cacheTable.getColumn(this.columns[4]).setCellRenderer(tableCellRenderer); + // TableCellRenderer for last modified column + cacheTable.getColumn(this.columns[5]).setCellRenderer(tableCellRenderer); c.weightx = 1; c.weighty = 1; @@ -139,7 +171,6 @@ public class CachePane extends JPanel { topPanel.add(scrollPane, c); this.add(topPanel, BorderLayout.CENTER); this.add(createButtonPanel(), BorderLayout.SOUTH); - } /** @@ -154,85 +185,45 @@ public class CachePane extends JPanel { List<JButton> buttons = new ArrayList<JButton>(); - JButton deleteButton = new JButton(Translator.R("CVCPButDelete")); + this.deleteButton = new JButton(Translator.R("CVCPButDelete")); deleteButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - FileLock fl = null; - File netxRunningFile = new File(config.getProperty(DeploymentConfiguration.KEY_USER_NETX_RUNNING_FILE)); - if (!netxRunningFile.exists()) { - try { - FileUtils.createParentDir(netxRunningFile); - FileUtils.createRestrictedFile(netxRunningFile, true); - } catch (IOException e1) { - OutputController.getLogger().log(OutputController.Level.ERROR_ALL, e1); - } - } - - try { - fl = FileUtils.getFileLock(netxRunningFile.getPath(), false, false); - } catch (FileNotFoundException e1) { - } - - int row = cacheTable.getSelectedRow(); - try { - if (fl == null) return; - if (row == -1 || row > cacheTable.getRowCount() - 1) - return; - int modelRow = cacheTable.convertRowIndexToModel(row); - DirectoryNode fileNode = ((DirectoryNode) cacheTable.getModel().getValueAt(modelRow, 0)); - if (fileNode.getFile().delete()) { - updateRecentlyUsed(fileNode.getFile()); - fileNode.getParent().removeChild(fileNode); - FileUtils.deleteWithErrMesg(fileNode.getInfoFile()); - ((DefaultTableModel) cacheTable.getModel()).removeRow(modelRow); - cacheTable.getSelectionModel().setSelectionInterval(row, row); - CacheDirectory.cleanParent(fileNode); - } - } catch (Exception exception) { - //ignore - } - - if (fl != null) { - try { - fl.release(); - fl.channel().close(); - } catch (IOException e1) { - OutputController.getLogger().log(OutputController.Level.ERROR_ALL, e1); - } - } - } - - private void updateRecentlyUsed(File f) { - File recentlyUsedFile = new File(location + File.separator + CacheLRUWrapper.CACHE_INDEX_FILE_NAME); - PropertiesFile pf = new PropertiesFile(recentlyUsedFile); - pf.load(); - Enumeration<Object> en = pf.keys(); - while (en.hasMoreElements()) { - String key = (String) en.nextElement(); - if (pf.get(key).equals(f.getAbsolutePath())) { - pf.remove(key); - } - } - pf.store(); + // Deleting may take a while, so indicate busy by cursor + parent.getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + // Disable dialog and buttons while deleting + deleteButton.setEnabled(false); + refreshButton.setEnabled(false); + doneButton.setEnabled(false); + // Delete on AWT thread after this action has been performed + // in order to allow the cache viewer to update itself + invokeLaterDelete(); } }); + deleteButton.setEnabled(false); buttons.add(deleteButton); - JButton refreshButton = new JButton(Translator.R("CVCPButRefresh")); + this.refreshButton = new JButton(Translator.R("CVCPButRefresh")); refreshButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - populateTable(); + // Disable all its controls when performing cacheTable refresh (populating) + deleteButton.setEnabled(false); + refreshButton.setEnabled(false); + doneButton.setEnabled(false); + // Populate cacheTable on AWT thread after this action event has been performed + invokeLaterPopulateTable(); } }); + refreshButton.setEnabled(false); buttons.add(refreshButton); - JButton doneButton = new JButton(Translator.R("ButDone")); + this.doneButton = new JButton(Translator.R("ButDone")); doneButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - parent.dispose(); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent( + new WindowEvent(parent, WindowEvent.WINDOW_CLOSING)); } }); @@ -251,6 +242,7 @@ public class CachePane extends JPanel { } doneButton.setPreferredSize(new Dimension(wantedWidth, wantedHeight)); + doneButton.setEnabled(false); rightPanel.add(doneButton); buttonPanel.add(leftPanel); buttonPanel.add(rightPanel); @@ -259,13 +251,140 @@ public class CachePane extends JPanel { } /** + * Posts an event to the event queue to delete the currently selected + * resource in {@link CachePane#cacheTable} after the {@code CachePane} and + * {@link CacheViewer} have been instantiated and painted. + * @see CachePane#cacheTable + */ + private final void invokeLaterDelete() { + EventQueue.invokeLater(new Runnable() { + public void run() { + try { + FileLock fl = null; + File netxRunningFile = new File(config.getProperty(DeploymentConfiguration.KEY_USER_NETX_RUNNING_FILE)); + if (!netxRunningFile.exists()) { + try { + FileUtils.createParentDir(netxRunningFile); + FileUtils.createRestrictedFile(netxRunningFile, true); + } catch (IOException e1) { + OutputController.getLogger().log(OutputController.Level.ERROR_ALL, e1); + } + } + + try { + fl = FileUtils.getFileLock(netxRunningFile.getPath(), false, false); + } catch (FileNotFoundException e1) { + } + + int row = cacheTable.getSelectedRow(); + try { + if (fl == null) return; + int modelRow = cacheTable.convertRowIndexToModel(row); + DirectoryNode fileNode = ((DirectoryNode) cacheTable.getModel().getValueAt(modelRow, 0)); + if (fileNode.getFile().delete()) { + updateRecentlyUsed(fileNode.getFile()); + fileNode.getParent().removeChild(fileNode); + FileUtils.deleteWithErrMesg(fileNode.getInfoFile()); + ((NonEditableTableModel) cacheTable.getModel()).removeRow(modelRow); + cacheTable.getSelectionModel().clearSelection(); + CacheDirectory.cleanParent(fileNode); + } + } catch (Exception exception) { + // ignore + } + + if (fl != null) { + try { + fl.release(); + fl.channel().close(); + } catch (IOException e1) { + OutputController.getLogger().log(OutputController.Level.ERROR_ALL, e1); + } + } + } catch (Exception exception) { + OutputController.getLogger().log(OutputController.Level.ERROR_DEBUG, exception); + } finally { + // If nothing selected then keep deleteButton disabled + if (!cacheTable.getSelectionModel().isSelectionEmpty()) deleteButton.setEnabled(true); + // Enable buttons + refreshButton.setEnabled(true); + doneButton.setEnabled(true); + // If cacheTable is empty disable it and set background + // color to indicate being disabled + if (cacheTable.getModel().getRowCount() == 0) { + cacheTable.setEnabled(false); + cacheTable.setBackground(SystemColor.control); + } + // Reset cursor + parent.getContentPane().setCursor(Cursor.getDefaultCursor()); + } + } + + private void updateRecentlyUsed(File f) { + File recentlyUsedFile = new File(location + File.separator + CacheLRUWrapper.CACHE_INDEX_FILE_NAME); + PropertiesFile pf = new PropertiesFile(recentlyUsedFile); + pf.load(); + Enumeration<Object> en = pf.keys(); + while (en.hasMoreElements()) { + String key = (String) en.nextElement(); + if (pf.get(key).equals(f.getAbsolutePath())) { + pf.remove(key); + } + } + pf.store(); + } + }); + } + + /** + * Posts an event to the event queue to populate the + * {@link CachePane#cacheTable} after the {@code CachePane} and + * {@link CacheViewer} have been instantiated and painted. + * @see CachePane#populateTable + */ + final void invokeLaterPopulateTable() { + EventQueue.invokeLater(new Runnable() { + public void run() { + try { + populateTable(); + // Disable cacheTable when no data to display, so no events are generated + if (cacheTable.getModel().getRowCount() == 0) { + cacheTable.setEnabled(false); + cacheTable.setBackground(SystemColor.control); + // No data in cacheTable, so nothing to delete + deleteButton.setEnabled(false); + } else { + cacheTable.setEnabled(true); + cacheTable.setBackground(SystemColor.text); + } + } catch (Exception exception) { + OutputController.getLogger().log(OutputController.Level.ERROR_DEBUG, exception); + } finally { + refreshButton.setEnabled(true); + doneButton.setEnabled(true); + } + } + }); + } + + /** * Populate the table with fresh data. Any manual updates to the cache * directory will be updated in the table. */ private void populateTable() { - ((DefaultTableModel) cacheTable.getModel()).setRowCount(0); //Clears the table - for (Object[] v : generateData(root)) - ((DefaultTableModel) cacheTable.getModel()).addRow(v); + try { + // Populating the cacheTable may take a while, so indicate busy by cursor + parent.getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + NonEditableTableModel tableModel; + (tableModel = (NonEditableTableModel)cacheTable.getModel()).setRowCount(0); //Clears the table + for (Object[] v : generateData(root)) tableModel.addRow(v); + } catch (Exception exception) { + OutputController.getLogger().log(OutputController.Level.ERROR_DEBUG, exception); + } finally { + // Reset cursor + parent.getContentPane().setCursor(Cursor.getDefaultCursor()); + } } /** @@ -283,12 +402,15 @@ public class CachePane extends JPanel { for (DirectoryNode type : identifier.getChildren()) { for (DirectoryNode domain : type.getChildren()) { for (DirectoryNode leaf : CacheDirectory.getLeafData(domain)) { - Object[] o = { leaf, - leaf.getFile().getAbsolutePath(), - type, - domain, - leaf.getFile().length(), - new SimpleDateFormat("MM/dd/yyyy").format(leaf.getFile().lastModified()) }; + final File f = leaf.getFile(); + Object[] o = { + leaf, + f.getParentFile(), + type, + domain, + f.length(), + new Date(f.lastModified()) + }; data.add(o); } } diff --git a/netx/net/sourceforge/jnlp/controlpanel/CacheViewer.java b/netx/net/sourceforge/jnlp/controlpanel/CacheViewer.java index fc63b60..bbdcad4 100644 --- a/netx/net/sourceforge/jnlp/controlpanel/CacheViewer.java +++ b/netx/net/sourceforge/jnlp/controlpanel/CacheViewer.java @@ -1,5 +1,5 @@ /* CacheViewer.java -- Display the GUI for viewing and deleting cache files. -Copyright (C) 2010 Red Hat +Copyright (C) 2013 Red Hat 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 @@ -22,7 +22,10 @@ import java.awt.Dimension; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; +import java.awt.KeyEventDispatcher; +import java.awt.KeyboardFocusManager; import java.awt.Toolkit; +import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; @@ -53,8 +56,11 @@ public class CacheViewer extends JDialog { */ public CacheViewer(DeploymentConfiguration config) { super((Frame) null, dialogTitle, true); // Don't need a parent. - setIconImages(ImageResources.INSTANCE.getApplicationImages()); this.config = config; + if (config == null) { + throw new IllegalArgumentException("config: " + config); + } + setIconImages(ImageResources.INSTANCE.getApplicationImages()); /* Prepare for adding components to dialog box */ Container contentPane = getContentPane(); @@ -70,11 +76,13 @@ public class CacheViewer extends JDialog { contentPane.add(topPanel, c); pack(); + this.topPanel.invokeLaterPopulateTable(); /* Set focus to default button when first activated */ WindowAdapter adapter = new WindowAdapter() { private boolean gotFocus = false; + @Override public void windowGainedFocus(WindowEvent we) { // Once window gets focus, set initial focus if (!gotFocus) { @@ -85,6 +93,33 @@ public class CacheViewer extends JDialog { }; addWindowFocusListener(adapter); + // Add a KeyEventDispatcher to dispatch events when this CacheViewer has focus + final CacheViewer cacheViewer = this; + KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher() { + /** + * Dispatches mainly the <code>VK_ESCAPE</code> key event to close + * the <code>CacheViewer</code> dialog. + * @return {@code true} after an {@link KeyEvent#VK_ESCAPE + * VK_ESCAPE} has been processed, otherwise {@code false} + * @see KeyEventDispatcher + */ + public boolean dispatchKeyEvent(final KeyEvent keyEvent) { + // Check if Esc key has been pressed + if (keyEvent.getKeyCode() == KeyEvent.VK_ESCAPE && + keyEvent.getID() == KeyEvent.KEY_PRESSED) { + // Exclude this key event from further processing + keyEvent.consume(); + // Remove this low-level KeyEventDispatcher + KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(this); + // Post close event to CacheViewer dialog + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent( + new WindowEvent(cacheViewer, WindowEvent.WINDOW_CLOSING)); + return true; + } + return false; + } + }); + initialized = true; } |