Mercurial > hg > release > icedtea-web-1.8
view netx/net/sourceforge/jnlp/controlpanel/CachePane.java @ 1538:67eb4fc36fa0
Added optional windows desktop integration
* AUTHORS: added Joel
* Makefile: Excluded (WindowsDesktopEntry.java) if mslinks are not included, added mslinks to UNIFIED_CLASSPATH_SEGMENTS included mslinks to windows and linux runtime libs, added MSLINKS_JAR to other composeclasspath calls
* NEWS: mentioned windows desktop support, mentioned listing of cache and operations via id.
* acinclude.m4: added check (IT_CHECK_FOR_MSLINKS) for optional mslinks.jar, strong warning printed if build is on windows
* configure.ac: call (IT_CHECK_FOR_MSLINKS)
* netx/net/sourceforge/jnlp/Launcher.java: new variable of (KEY_JAVAWS_LOCATION) to replace hardcoded icedtea-web.bin.location over netx.
* netx/net/sourceforge/jnlp/OptionsDefinitions.java: re-declared clear cache to take none or one argument. Added Xcacheids switch for listing the cache (works with verbose)
* netx/net/sourceforge/jnlp/cache/CacheDirectory.java: refactored hardcoded ".info" to constant.
* netx/net/sourceforge/jnlp/cache/CacheEntry.java: introduced KEY_JNLP_PATH and used to set jnlp-path attribute
* netx/net/sourceforge/jnlp/cache/CacheLRUWrapper.java: hide private constructor, declared and provided (windowsShortcutList)
* netx/net/sourceforge/jnlp/cache/CacheUtil.java: extracted and used (checkToClearCache). Added second method clearCache with arg to clear only specific part of cache. Clear cache also alerts windows desktop files now via new removeWindowsShortcuts. Added methods to lists ids and details from cache listCacheIds and getCacheIds. Included new inner class CacheId to encapsualte various types of id - CacheJnlpId and CacheDomainId now.
* netx/net/sourceforge/jnlp/cache/DirectoryNode.java: only adapted to .info refactoring
* netx/net/sourceforge/jnlp/cache/ResourceDownloader.java: Save main argument, or jnlp argument or html argument to jnlp-path .info entry if found.
* netx/net/sourceforge/jnlp/controlpanel/CacheAppViewer.java: gui to itweb-settings cache pane to allow comfortable listing of ids and deleting via those grouping. New file.
* netx/net/sourceforge/jnlp/controlpanel/CachePane.java: added logic to show .info details for each file shown by cache viewer. (generateData) made jnlp-path aware, made public and reused several times
* netx/net/sourceforge/jnlp/controlpanel/TemporaryInternetFilesPanel.java: added button to show dilog which is deleting by id
* netx/net/sourceforge/jnlp/resources/Messages.properties: added BXclearcache BXSingleCacheCleared BXSingleCacheClearNotFound BXSingleCacheMoreThenOneId BXSingleCacheFileCount BXcacheids NOAnonorone WinDesktopError. Modified BXclearcache. Improved EXAWdesktopWants EXAWdesktopDontWants EXAWsubmenu EXAWmenuWants EXAWmenuDontWants EXAWrememberByApp EXAWrememberByPage EXAWrememberByAppTooltip EXAWbrowsersTolltip SDesktopShortcut
* netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java: added if isWindows reflective calls to WindowsDesktopEntry and original XdesktopEntry work moved to else part.
* netx/net/sourceforge/jnlp/runtime/Boot.java: now offer getter for optionParser (so it van be reused in ResourceDownloader) Added understanding to Xcacheids. Understanding to Xclearcache adapted to its new optional argument
* netx/net/sourceforge/jnlp/util/GenericDesktopEntry.java: interface for (future) WindowsDesktopEntry and XDesktopEntry unification
* netx/net/sourceforge/jnlp/util/WindowsDesktopEntry.java: new file, implementation fo windos desktop integration via lnk files generated by mslinks.jar. Unlike XDesktop integration, it swarms also uninstall shortcuts.
* netx/net/sourceforge/jnlp/util/XDesktopEntry.java: Mostly adapted to refactorings. Extracted extraction of favicon to method, reused, and improved to try more locations on server.
* netx/net/sourceforge/jnlp/util/optionparser/OptionParser.java: adapted to refactorings
* shell-launcher/launchers.bat.in: mslinks included in bootclasspath
* tests/netx/unit/net/sourceforge/jnlp/cache/CacheUtilTest.java: addd tests for CacheId
* tests/netx/unit/net/sourceforge/jnlp/util/XDesktopEntryTest.java: Added tests for favicon extracti
author | Jiri Vanek <jvanek@redhat.com> |
---|---|
date | Tue, 15 Jan 2019 16:12:46 +0100 |
parents | b99a42be9966 |
children |
line wrap: on
line source
/* CachePane.java -- Displays the specified folder and allows modification to its content. 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 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 net.sourceforge.jnlp.controlpanel; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; 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.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.channels.FileLock; import java.text.DateFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Comparator; import java.util.Date; import java.util.Enumeration; import java.util.List; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.ListSelectionModel; 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; import net.sourceforge.jnlp.cache.CacheEntry; import net.sourceforge.jnlp.cache.CacheUtil; import net.sourceforge.jnlp.cache.DirectoryNode; import net.sourceforge.jnlp.config.DeploymentConfiguration; import net.sourceforge.jnlp.config.PathsAndFiles; import net.sourceforge.jnlp.runtime.Translator; import net.sourceforge.jnlp.util.FileUtils; import net.sourceforge.jnlp.util.PropertiesFile; import net.sourceforge.jnlp.util.StreamUtils; import net.sourceforge.jnlp.util.logging.OutputController; import net.sourceforge.jnlp.util.ui.NonEditableTableModel; import net.sourceforge.swing.SwingUtils; public class CachePane extends JPanel { final JDialog parent; final DeploymentConfiguration config; private JComponent defaultFocusComponent; String[] columns = { Translator.R("CVCPColName"), Translator.R("CVCPColPath"), Translator.R("CVCPColType"), Translator.R("CVCPColDomain"), Translator.R("CVCPColSize"), Translator.R("CVCPColLastModified"), CacheEntry.KEY_JNLP_PATH }; JTable cacheTable; private JButton deleteButton, refreshButton, doneButton, cleanAll, infoButton; /** * Creates a new instance of the CachePane. * * @param parent The parent dialog that uses this pane. * @param config configuration tobe worked on */ public CachePane(JDialog parent, DeploymentConfiguration config) { super(new BorderLayout()); this.parent = parent; this.config = config; addComponents(); } /** * Add components to the pane. */ private void addComponents() { JPanel topPanel = new JPanel(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; TableModel model = new NonEditableTableModel(columns, 0); cacheTable = new JTable(model); cacheTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); cacheTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override final public void valueChanged(ListSelectionEvent listSelectionEvent) { // If no row has been selected, disable the delete button, else enable it if (cacheTable.getSelectionModel().isSelectionEmpty()) { deleteButton.setEnabled(false); infoButton.setEnabled(false); } else { deleteButton.setEnabled(true); infoButton.setEnabled(true); } } }); cacheTable.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN); cacheTable.setPreferredScrollableViewportSize(new Dimension(600, 200)); cacheTable.setFillsViewportHeight(true); JScrollPane scrollPane = new JScrollPane(cacheTable); TableRowSorter<TableModel> tableSorter = new TableRowSorter<>(model); final Comparator<Comparable<?>> comparator = new Comparator<Comparable<?>>() { // General purpose Comparator @Override @SuppressWarnings("unchecked") public final int compare(final Comparable a, final Comparable b) { return a.compareTo(b); } }; 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 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; } }; // 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; c.gridx = 0; c.gridy = 0; topPanel.add(scrollPane, c); this.add(topPanel, BorderLayout.CENTER); this.add(createButtonPanel(), BorderLayout.SOUTH); } /** * Create the buttons panel. * * @return JPanel containing the buttons. */ private Component createButtonPanel() { JPanel buttonPanel = new JPanel(new GridLayout(1, 0)); JPanel leftPanel = new JPanel(new FlowLayout(FlowLayout.LEADING)); JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING)); List<JButton> buttons = new ArrayList<>(); this.infoButton = new JButton(Translator.R("ButMoreInformation")); infoButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JDialog jd = new JDialog(parent, true); JTextArea t = new JTextArea(); t.setEditable(false); int row = cacheTable.getSelectedRow(); try { int modelRow = cacheTable.convertRowIndexToModel(row); DirectoryNode fileNode = ((DirectoryNode) cacheTable.getModel().getValueAt(modelRow, 0)); File selectedFile = fileNode.getFile(); File infoFile = new File(selectedFile + CacheDirectory.INFO_SUFFIX); String info = StreamUtils.readStreamAsString(new FileInputStream(infoFile), true); t.setText(info); } catch (Exception ex) { t.setText(ex.toString()); } jd.add(t); jd.setSize(300, 300); jd.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); jd.setVisible(true); } }); infoButton.setEnabled(false); buttons.add(infoButton); this.deleteButton = new JButton(Translator.R("CVCPButDelete")); deleteButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { disableButtons(); // 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); this.cleanAll = new JButton(Translator.R("CVCPCleanCache")); cleanAll.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { disableButtons(); // Delete on AWT thread after this action has been performed // in order to allow the cache viewer to update itself invokeLaterDeleteAll(); } }); buttons.add(cleanAll); this.refreshButton = new JButton(Translator.R("CVCPButRefresh")); refreshButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { disableButtons(); // Populate cacheTable on AWT thread after this action event has been performed invokeLaterPopulateTable(); } }); refreshButton.setEnabled(false); buttons.add(refreshButton); this.doneButton = new JButton(Translator.R("ButDone")); doneButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent( new WindowEvent(parent, WindowEvent.WINDOW_CLOSING)); } }); int maxWidth = 0; int maxHeight = 0; for (JButton button : buttons) { maxWidth = Math.max(button.getMinimumSize().width, maxWidth); maxHeight = Math.max(button.getMinimumSize().height, maxHeight); } int wantedWidth = maxWidth + 10; int wantedHeight = maxHeight; for (JButton button : buttons) { button.setPreferredSize(new Dimension(wantedWidth, wantedHeight)); leftPanel.add(button); } doneButton.setPreferredSize(new Dimension(wantedWidth, wantedHeight)); doneButton.setEnabled(false); rightPanel.add(doneButton); buttonPanel.add(leftPanel); buttonPanel.add(rightPanel); return buttonPanel; } /** * 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 void invokeLaterDelete() { SwingUtils.invokeLater(new Runnable() { @Override public void run() { try { FileLock fl = null; File netxRunningFile = new File(PathsAndFiles.MAIN_LOCK.getFullPath(config)); 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) { JOptionPane.showMessageDialog(parent, Translator.R("CCannotClearCache")); 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 { restoreDisabled(); } } private void updateRecentlyUsed(File f) { File recentlyUsedFile = new File(PathsAndFiles.getRecentlyUsedFile().getFullPath(config)); 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(); } }); } private void invokeLaterDeleteAll() { SwingUtils.invokeLater(new Runnable() { @Override public void run() { try { visualCleanCache(parent); populateTable(); } catch (Exception exception) { OutputController.getLogger().log(OutputController.Level.ERROR_DEBUG, exception); } finally { restoreDisabled(); } } }); } /** * 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() { SwingUtils.invokeLater(new Runnable() { @Override 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); infoButton.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); cleanAll.setEnabled(true); } } }); } /** * Populate the table with fresh data. Any manual updates to the cache * directory will be updated in the table. */ private void populateTable() { 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()) { tableModel.addRow(v); } } catch (Exception exception) { OutputController.getLogger().log(OutputController.Level.ERROR_DEBUG, exception); } finally { // Reset cursor parent.getContentPane().setCursor(Cursor.getDefaultCursor()); } } /** * This creates the data for the table. * * @return ArrayList containing an Object array of data for each row in the * table. */ public static ArrayList<Object[]> generateData() { DirectoryNode root = new DirectoryNode("Root", PathsAndFiles.CACHE_DIR.getFile(), null); CacheDirectory.getDirStructure(root); ArrayList<Object[]> data = new ArrayList<>(); for (DirectoryNode identifier : root.getChildren()) { for (DirectoryNode type : identifier.getChildren()) { for (DirectoryNode domain : type.getChildren()) { //after domain, there is optional port dir. It is skipped here (as is skipped path on domain) for (DirectoryNode leaf : CacheDirectory.getLeafData(domain)) { final File f = leaf.getFile(); PropertiesFile pf = new PropertiesFile(new File(f.toString() + CacheDirectory.INFO_SUFFIX)); // if jnlp-path in .info equals path of app to delete mark to delete String jnlpPath = pf.getProperty(CacheEntry.KEY_JNLP_PATH); Object[] o = { leaf, f.getParentFile(), type, domain, f.length(), new Date(f.lastModified()), jnlpPath }; data.add(o); } } } } return data; } /** * Put focus onto default button. */ public void focusOnDefaultButton() { if (defaultFocusComponent != null) { defaultFocusComponent.requestFocusInWindow(); } } public void disableButtons() { // may take a while, so indicate busy by cursor parent.getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); // Disable dialog and buttons while operating deleteButton.setEnabled(false); infoButton.setEnabled(false); refreshButton.setEnabled(false); doneButton.setEnabled(false); cleanAll.setEnabled(false); } public void restoreDisabled() { cleanAll.setEnabled(true); // If nothing selected then keep deleteButton disabled if (!cacheTable.getSelectionModel().isSelectionEmpty()) { deleteButton.setEnabled(true); infoButton.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()); } public static void visualCleanCache(Component parent) { try { boolean success = CacheUtil.clearCache(); if (!success) { JOptionPane.showMessageDialog(parent, Translator.R("CCannotClearCache")); } } catch (Exception ex) { JOptionPane.showMessageDialog(parent, Translator.R("CCannotClearCache")); } } }