oburn 2003/02/25 04:51:16 Added: src/java/org/apache/log4j/chainsaw MyTableColumnModel.java PreferenceSet.java Preferences.java PreferencesDialog.java RecentFilesMenu.java Log: Patch from Raymond DeCampo: Chainsaw is now capable of remembering the size and position of the main window, the position of the table/details divider, the width and order of all the columns, whether or not each column is displayed, the state of the filtes and a recent files list. The number of files in the recent files list is configurable, as is the whether the state of the filters is preserved. These preferences are accessible from the File->Preferences menu option. The preference dialog also allows the user to select which columns are shown. Mnemonic shortcuts have been added to all menu items. The recent files submenu is under the File menu. The preferences are stored under the user's home directorey by default but the preference file and any individual preference may be overridden by the system properties. Chainsaw will handle the absence of the preferences file by creating it; no errors should arise from any preference not being defined. Revision Changes Path 1.1 jakarta-log4j/src/java/org/apache/log4j/chainsaw/MyTableColumnModel.java Index: MyTableColumnModel.java =================================================================== /* * ============================================================================ * The Apache Software License, Version 1.1 * ============================================================================ * * Copyright (C) 1999 The Apache Software Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without modifica- * tion, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The end-user documentation included with the redistribution, if any, must * include the following acknowledgment: "This product includes software * developed by the Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, if * and wherever such third-party acknowledgments normally appear. * * 4. The names "log4j" and "Apache Software Foundation" must not be used to * endorse or promote products derived from this software without prior * written permission. For written permission, please contact * [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache", nor may * "Apache" appear in their name, without prior written permission of the * Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals * on behalf of the Apache Software Foundation. For more information on the * Apache Software Foundation, please see <http://www.apache.org/>. * */ package org.apache.log4j.chainsaw; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.swing.table.DefaultTableColumnModel; import javax.swing.table.TableColumn; import org.apache.log4j.Logger; /** * An extension of TableColumnModel for log4j events. Primarily this class * manages the preferences for the table. * * @author <a href="mailto:[EMAIL PROTECTED]">Ray DeCampo</a> */ class MyTableColumnModel extends DefaultTableColumnModel { /** Prefix for all properties referenced by this class. */ public static final String PROP_PREFIX = Preferences.PROP_PREFIX + ".table"; /** Column order property */ public static final String ORDER_PROPERTY = PROP_PREFIX + ".columns.order"; /** Column width property */ public static final String COLUMN_WIDTH_PROPERTY = "width"; /** Column visibility property */ public static final String COLUMN_VISIBLE_PROPERTY = "visible"; /** Logger for the class */ private static final Logger LOG = Logger.getLogger(MyTableColumnModel.class); private static final Preferences PREFS = Preferences.getInstance(); /** Map of TableColumns to PreferenceSets */ private final Map mColPrefMap = new HashMap(); private final MyTableModel mTableModel; /** * Construct a MyTableColumnModel. * * @param tableModel table model to work with */ public MyTableColumnModel(MyTableModel tableModel) { mTableModel = tableModel; } /** * Load the properties from the * [EMAIL PROTECTED] org.apache.log4j.chainsaw.Preferences} and apply them to the * model. */ public void loadPrefs() { // Keep a separate list of columns to remove to avoid concurrent // modification final List toRemove = new ArrayList(getColumnCount()); for (int i = 0; i < getColumnCount(); i++) { TableColumn col = getColumn(i); PreferenceSet colPrefSet = PREFS.getPreferenceSet(PROP_PREFIX, mTableModel.getColumnName(i)); putColumnPreferences(col, colPrefSet); int width = colPrefSet.getInteger(COLUMN_WIDTH_PROPERTY, -1); if (width >= 0) { col.setPreferredWidth(width); } boolean visible = colPrefSet.getBoolean(COLUMN_VISIBLE_PROPERTY, true); if (!visible) { LOG.info("Hiding column " + mTableModel.getColumnName(i)); toRemove.add(col); } } // Order the columns properly String[] colOrder = getColumnOrder(); for (int i = 0; i < colOrder.length; i++) { int index = getColumnIndex(colOrder[i]); super.moveColumn(index, i); } // Remove the columns that should not be visible for (int i = 0; i < toRemove.size(); i++) { removeColumn((TableColumn) toRemove.get(i)); } } /** * Update the [EMAIL PROTECTED] org.apache.log4j.chainsaw.Preferences} based on the * state of the model. */ public void savePrefs() { for (int i = 0; i < getColumnCount(); i++) { TableColumn col = getColumn(i); PreferenceSet colPrefSet = getColumnPreferences(col); colPrefSet.setInteger(COLUMN_WIDTH_PROPERTY, col.getWidth()); colPrefSet.setBoolean(COLUMN_VISIBLE_PROPERTY, true); } } /** [EMAIL PROTECTED] */ public void addColumn(TableColumn column) { PreferenceSet colPrefSet = getColumnPreferences(column); if (colPrefSet != null) { colPrefSet.setBoolean(COLUMN_VISIBLE_PROPERTY, true); } super.addColumn(column); } /** [EMAIL PROTECTED] */ public void removeColumn(TableColumn column) { PreferenceSet colPrefSet = getColumnPreferences(column); if (colPrefSet != null) { colPrefSet.setBoolean(COLUMN_VISIBLE_PROPERTY, false); } super.removeColumn(column); } /** [EMAIL PROTECTED] */ public void moveColumn(int columnIndex, int newIndex) { super.moveColumn(columnIndex, newIndex); saveColumnOrder(); } /** * Get the [EMAIL PROTECTED] javax.swing.table.TableColumn}s available for the * logging event table. All columns are returned iiregardless of whether * thay are or are not visible. * * @return the set of available columns */ public Set getAvailableColumns() { return mColPrefMap.keySet(); } /** * Get the [EMAIL PROTECTED] org.apache.log4j.chainsaw.PrefenceSet} for a given * column. * * @param col the column to get the preferences for * * @return the set of preferences for the column */ public PreferenceSet getColumnPreferences(TableColumn col) { return (PreferenceSet) mColPrefMap.get(col); } /* Store the column preferences in the map. */ private void putColumnPreferences(TableColumn col, PreferenceSet colPrefSet) { mColPrefMap.put(col, colPrefSet); } /* Determine the order of the columns based on the preferences. */ private String[] getColumnOrder() { String colOrder = PREFS.getProperty(ORDER_PROPERTY); if (colOrder == null) { return new String[0]; } List result = new ArrayList(6); int last = 0; int comma = -1; while ( (last < colOrder.length()) && ((comma = colOrder.indexOf(',', last)) >= 0)) { result.add(colOrder.substring(last, comma)); last = comma + 1; } if (last < colOrder.length()) { result.add(colOrder.substring(last)); } return (String[]) result.toArray(new String[result.size()]); } /** Save the current column order to the preferences */ private void saveColumnOrder() { StringBuffer colList = new StringBuffer(45); for (int i = 0; i < getColumnCount(); i++) { if (i > 0) { colList.append(','); } PreferenceSet colPrefSet = getColumnPreferences(getColumn(i)); colList.append(colPrefSet.getName()); } PREFS.setProperty(ORDER_PROPERTY, colList.toString()); } } 1.1 jakarta-log4j/src/java/org/apache/log4j/chainsaw/PreferenceSet.java Index: PreferenceSet.java =================================================================== /* * ============================================================================ * The Apache Software License, Version 1.1 * ============================================================================ * * Copyright (C) 1999 The Apache Software Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without modifica- * tion, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The end-user documentation included with the redistribution, if any, must * include the following acknowledgment: "This product includes software * developed by the Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, if * and wherever such third-party acknowledgments normally appear. * * 4. The names "log4j" and "Apache Software Foundation" must not be used to * endorse or promote products derived from this software without prior * written permission. For written permission, please contact * [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache", nor may * "Apache" appear in their name, without prior written permission of the * Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals * on behalf of the Apache Software Foundation. For more information on the * Apache Software Foundation, please see <http://www.apache.org/>. * */ package org.apache.log4j.chainsaw; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * A PreferenceSet represents a set of properties within a * [EMAIL PROTECTED] org.apache.log4j.chainsaw.Preferences} object that share a given * prefix. The PrefenceSet gives convenient access to the properties * without need to explicitly specify the prefix. * * @author <a href="mailto:[EMAIL PROTECTED]">Raymond DeCampo</a> */ class PreferenceSet { /** the prefix */ private String mPropPrefix; /** the name of the set */ private String mName; /** the Preferences object containing the properties */ private Preferences mPrefs; /** * Create a PreferenceSet with the given prefix, name and * [EMAIL PROTECTED] org.apache.log4j.chainsaw.Preferences}. The prefix and name * are concatenated to obtain the full prefix. * <p> * For example, if the propPrefix is "org.apache.log4j.chainsaw" * and the name is "table" the full prefix is * "org.apache.log4j.chainsaw.table". The full prefix is * automatically prepended to any property name passed to any method. * * @param propPrefix the prefix to all the properties * @param name the name of the set; supplements the prefix * @param prefs the Preferences object containing the properties */ public PreferenceSet(String propPrefix, String name, Preferences prefs) { mPropPrefix = propPrefix; mName = name; mPrefs = prefs; } /** * Get the name of the set. * * @return the name of the set */ public String getName() { return mName; } /** * Get the value of a property. The full prefix is prepended to the * property and then the value is obtained from the * [EMAIL PROTECTED] org.apache.log4j.chainsaw.Preferences} object. * * @param property the property to retrieve * * @return the value of the property */ public String getProperty(String property) { return mPrefs.getProperty(getFullPrefix() + property); } /** * Get the value of a given property. If the property is not defined then * the default value is returned. The full prefix is prepended to the * property and then the value is obtained from the * [EMAIL PROTECTED] org.apache.log4j.chainsaw.Preferences} object. * * @param property the property to retrieve * @param def the default value * * @return the value of the property or def if the property is not defined */ public String getProperty(String property, String def) { return mPrefs.getProperty(getFullPrefix() + property, def); } /** * Set the value of a property. The full prefix is prepended to the * property and then the value is set in the * [EMAIL PROTECTED] org.apache.log4j.chainsaw.Preferences} object. * * @param property the property to set * @param value the new value */ public void setProperty(String property, String value) { if (value == null) { value = ""; } mPrefs.setProperty(getFullPrefix() + property, value); } /** * Get the value of the given property as an integer. If the property is * not defined or cannot be parsed into an integer then def is returned. * The full prefix is prepended to the property and then the value is * obtained from the [EMAIL PROTECTED] org.apache.log4j.chainsaw.Preferences} object. * * @param property the property to retrieve * @param def the default value * * @return the value of the property or def if the property cannot be * expressed as an integer */ public int getInteger(String property, int def) { return mPrefs.getInteger(getFullPrefix() + property, def); } /** * Set the value of a property. The full prefix is prepended to the * property and then the value is set in the * [EMAIL PROTECTED] org.apache.log4j.chainsaw.Preferences} object. * * @param property the property to set * @param value the new value */ public void setInteger(String property, int value) { mPrefs.setInteger(getFullPrefix() + property, value); } /** * Get the value of the given property as a boolean. If the property is * not defined or cannot be parsed into a boolean then def is returned. * The full prefix is prepended to the property and then the value is * obtained from the [EMAIL PROTECTED] org.apache.log4j.chainsaw.Preferences} object. * * @param property the property to retrieve * @param def the default value * * @return the value of the property or def if the property cannot be * expressed as a boolean */ public boolean getBoolean(String property, boolean def) { return mPrefs.getBoolean(getFullPrefix() + property, def); } /** * Set the value of a property. The full prefix is prepended to the * property and then the value is set in the * [EMAIL PROTECTED] org.apache.log4j.chainsaw.Preferences} object. * * @param property the property to set * @param value the new value */ public void setBoolean(String property, boolean value) { mPrefs.setBoolean(getFullPrefix() + property, value); } /** * Remove this set of preferences from the * [EMAIL PROTECTED] org.apache.log4j.chainsaw.Preferences} object. */ public void remove() { List toRemove = new ArrayList(6); for (Iterator keys = mPrefs.keySet().iterator(); keys.hasNext();) { String key = (String) keys.next(); if (key.startsWith(getFullPrefix())) { toRemove.add(key); } } for (Iterator keys = toRemove.iterator(); keys.hasNext();) { mPrefs.remove(keys.next()); } } /** * Get the full prefix for properties * * @return the full prefix */ private String getFullPrefix() { return mPropPrefix + "." + mName + "."; } } 1.1 jakarta-log4j/src/java/org/apache/log4j/chainsaw/Preferences.java Index: Preferences.java =================================================================== /* * ============================================================================ * The Apache Software License, Version 1.1 * ============================================================================ * * Copyright (C) 1999 The Apache Software Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without modifica- * tion, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The end-user documentation included with the redistribution, if any, must * include the following acknowledgment: "This product includes software * developed by the Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, if * and wherever such third-party acknowledgments normally appear. * * 4. The names "log4j" and "Apache Software Foundation" must not be used to * endorse or promote products derived from this software without prior * written permission. For written permission, please contact * [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache", nor may * "Apache" appear in their name, without prior written permission of the * Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals * on behalf of the Apache Software Foundation. For more information on the * Apache Software Foundation, please see <http://www.apache.org/>. * */ package org.apache.log4j.chainsaw; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Properties; import org.apache.log4j.Logger; /** * Manages user preferences for the Chainsaw application. * <p> * Unless overriden by the [EMAIL PROTECTED] #PROP_FILE} system property, the properties * file used is ${user.home}/.chainsaw/preferences.properties, where * ${user.home} is the value of the system property "user.home". * If necessary, the file and the .chainsaw directory will be created. * <p> * Any property in the file may be overridden by defining the property as * a system property (either programmtaically or by using the -D option to * java). * * @author <a href="mailto:[EMAIL PROTECTED]">Raymond DeCampo</a> */ class Preferences extends Properties { /** Prefix to all properties related to Chainsaw. */ public static final String PROP_PREFIX = "org.apache.log4j.chainsaw"; /** System property used to override the default property file. */ public static final String PROP_FILE = PROP_PREFIX + ".properties"; /** Property defining the list of recent files. */ public static final String FILES_PROPERTY = PROP_PREFIX + ".files"; /** Property defining the maximum number of recent files. */ public static final String MAX_FILES_PROPERTY = PROP_PREFIX + ".max.files"; /** used to log messages **/ private static final Logger LOG = Logger.getLogger(Preferences.class); /** * Single global instance based on property file in user.home or by file * indicated by [EMAIL PROTECTED] #PROP_FILE} system property. **/ private static Preferences sInstance = null; /** The actual preferences file */ private File prefFile = null; /** List of Strings, the recent files list */ private final LinkedList files = new LinkedList(); /** Maximum number of entries in th recent files menu */ private int mMaxFiles = 5; /** The recent files menu */ private RecentFilesMenu mRecentFilesMenu = null; /** Private constructor for when no preference file is found */ private Preferences() { } /** * Private constructor accepting a file * * @param preferencesFile file containing preferences * * @throws IOException if the preferencesFile could not be read */ private Preferences(File preferencesFile) throws IOException { prefFile = preferencesFile; InputStream in = null; try { LOG.info("Loading properties from " + prefFile); in = new BufferedInputStream(new FileInputStream(prefFile)); load(in); } finally { if (in != null) { in.close(); } } } /** * Get the global instance of the Preferences class. * * @return the global instance of the Preferences class */ public static Preferences getInstance() { if (sInstance == null) { sInstance = createInstance(); } return sInstance; } /** * Get the value of the given property. If the property is defined as a * system property, that value will be returned. * * @param property the property to retrieve * * @return the value of the given property */ public String getProperty(String property) { String result = System.getProperty(property); if (result == null) { result = super.getProperty(property); } return result; } /** * Get the value of a given property. If the property is not defined then * the default value is returned. * * @param property the property to retrieve * @param def the default value * * @return the value of the property or def if the property is not defined */ public String getProperty(String property, String def) { String result = getProperty(property); if (result == null) { result = def; } return result; } /** * Get the value of the given property as an integer. If the property is * not defined or cannot be parsed into an integer then def is returned. * * @param property the property to retrieve * @param def the default value * * @return the value of the property or def if the property cannot be * expressed as an integer */ public int getInteger(String property, int def) { int result = def; String strValue = getProperty(property); try { if (strValue != null) { result = Integer.parseInt(strValue); } } catch (final NumberFormatException nfe) { // just use default value } return result; } /** * Set the value of a property. * * @param property the property to set * @param value the value to set it to */ public void setInteger(String property, int value) { setProperty(property, String.valueOf(value)); } /** * Get the value of the given property as a boolean. If the property is * not defined or cannot be parsed into a boolean then def is returned. * * @param property the property to retrieve * @param def the default value * * @return the value of the property or def if the property cannot be * expressed as a boolean */ public boolean getBoolean(String property, boolean def) { boolean result = def; String strValue = getProperty(property); if (strValue != null) { result = Boolean.valueOf(strValue).booleanValue(); } return result; } /** * Set the value of a property. * * @param property the property to set * @param value the value to set it to */ public void setBoolean(String property, boolean value) { setProperty(property, String.valueOf(value)); } /** * Save the preferences to the preferences file. * * @throws IOException if an error occurs while writing to the file */ public void save() throws IOException { if (prefFile != null) { OutputStream out = null; try { LOG.info("Saving preferences to " + prefFile); out = new BufferedOutputStream(new FileOutputStream(prefFile)); store(out, "LOG4J Chainsaw property file"); } finally { try { if (out != null) { out.close(); } } catch (final IOException ioe) { LOG.warn( "Error closing preferences file " + prefFile.getPath(), ioe); } } } } /** * Notify the preferences that a file has been loaded for integration into * the recent files list. * * @param filename the file that was loaded */ public void fileLoaded(String filename) { files.remove(filename); files.addFirst(filename); if (files.size() > mMaxFiles) { files.removeLast(); } rebuildRecentFilesData(); } /** * Get the maximum number files in the recent files list. * * @return the maximum number files in the recent files list */ public int getMaxFiles() { return mMaxFiles; } /** * Set the maximum number of files in the recent files list. If the value * is less than the current size of the list, the list will be truncated. * * @param newMaxFiles the new value for the maximum number of files in the * recent files list */ public void setMaxFiles(int newMaxFiles) { mMaxFiles = newMaxFiles; setInteger(MAX_FILES_PROPERTY, mMaxFiles); while (files.size() > mMaxFiles) { files.removeLast(); } rebuildRecentFilesData(); } /** * Set the recent files menu. The menu registered will be notified of * changes in the recent files list. * * @param newMenu the new recent files menu */ public void setRecentFilesMenu(RecentFilesMenu newMenu) { mRecentFilesMenu = newMenu; } /** * Get a [EMAIL PROTECTED] PreferenceSet} based on this Preferences object. * * @param prefix the prefix for the set * @param name the name of the set * * @return the [EMAIL PROTECTED] PreferenceSet} based on this Preferences object with * the given prefix and name */ public PreferenceSet getPreferenceSet(String prefix, String name) { return new PreferenceSet(prefix, name, this); } /** * Get an unmodifiable list of the recent files. Entries in the list are * [EMAIL PROTECTED] java.lang.String}s. * * @return an unmodifiable list of strings representing the recent files. */ public List getRecentFiles() { return Collections.unmodifiableList(files); } /* Create the single instance */ private static Preferences createInstance() { String filename = getPropertyFilename(); File file = new File(filename); if (!file.exists()) { LOG.debug("Creating preferences file " + filename); try { file.createNewFile(); } catch (final IOException ioe) { LOG.warn("Could not create file " + filename, ioe); } } if (file.exists()) { try { sInstance = new Preferences(file); } catch (final IOException ioe) { LOG.warn("Could not read file " + filename, ioe); } } if (sInstance == null) { sInstance = new Preferences(); } sInstance.load(); return sInstance; } /* Determine the property file to use */ private static String getPropertyFilename() { String filename = System.getProperty(PROP_FILE); if (filename == null) { String userHome = System.getProperty("user.home", ""); String dirName = userHome + File.separator + ".chainsaw"; File dir = new File(dirName); if (!dir.exists()) { LOG.debug("Creating preferences directory."); if (!dir.mkdir()) { LOG.warn("Could not create directory " + dir.getPath()); } } filename = dirName + File.separator + "preferences.properties"; } return filename; } /** Load the preferences from the file. */ private void load() { mMaxFiles = getInteger(MAX_FILES_PROPERTY, 5); loadFiles(); } /** Load the recent files list. */ private void loadFiles() { final char[] ch = getProperty(FILES_PROPERTY, "").toCharArray(); final StringBuffer filename = new StringBuffer(ch.length); int fileCount = 0; // The recent files list is kept as a comma-separated list of filenames // Commas are escaped by doubling for (int i = 0; (i < ch.length) && (fileCount < mMaxFiles); i++) { if (ch[i] == ',') { if (((i + 1) < ch.length) && (ch[i + 1] == ',')) { // Double comma equals one escaped comma filename.append(','); i++; } else { // denotes the end of a filename files.add(filename.toString()); fileCount++; filename.setLength(0); } } else { filename.append(ch[i]); } } if ((filename.length() > 0) && (fileCount < mMaxFiles)) { // handle last file files.add(filename.toString()); } if (mRecentFilesMenu != null) { mRecentFilesMenu.rebuild(); } } /** Rebuild the recent files list property and menu */ private void rebuildRecentFilesData() { StringBuffer fileList = new StringBuffer(); boolean first = true; Iterator fileIter = files.iterator(); while (fileIter.hasNext()) { String filename = (String) fileIter.next(); if (!first) { fileList.append(','); } // Add the file name to the list int index = 0; int lastIndex = 0; while ((index >= 0) && (lastIndex < filename.length())) { index = filename.indexOf(',', lastIndex); if (index >= 0) { // Append the filename, up to the and including the comma fileList.append(filename.substring(lastIndex, index + 1)); fileList.append(','); lastIndex = index + 1; } else { // Append the rest of the filename fileList.append(filename.substring(lastIndex)); } } first = false; } setProperty(FILES_PROPERTY, fileList.toString()); if (mRecentFilesMenu != null) { mRecentFilesMenu.rebuild(); } } } 1.1 jakarta-log4j/src/java/org/apache/log4j/chainsaw/PreferencesDialog.java Index: PreferencesDialog.java =================================================================== /* * ============================================================================ * The Apache Software License, Version 1.1 * ============================================================================ * * Copyright (C) 1999 The Apache Software Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without modifica- * tion, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The end-user documentation included with the redistribution, if any, must * include the following acknowledgment: "This product includes software * developed by the Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, if * and wherever such third-party acknowledgments normally appear. * * 4. The names "log4j" and "Apache Software Foundation" must not be used to * endorse or promote products derived from this software without prior * written permission. For written permission, please contact * [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache", nor may * "Apache" appear in their name, without prior written permission of the * Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals * on behalf of the Apache Software Foundation. For more information on the * Apache Software Foundation, please see <http://www.apache.org/>. * */ package org.apache.log4j.chainsaw; import java.awt.BorderLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.table.TableColumn; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.PlainDocument; /** * The PreferencesDialog presenta graphical means for editing * [EMAIL PROTECTED] org.apache.log4j.chainsaw.Preferences}. * * @author <a href="mailto:[EMAIL PROTECTED]">Raymond DeCampo</a> */ class PreferencesDialog extends JDialog { private static final Preferences PREFS = Preferences.getInstance(); private final MyTableColumnModel mColModel; private final JTextField mMaxFilesField = new JTextField(6); private final JCheckBox mSaveFilters = new JCheckBox("Save filters?"); /** Map relating TableColumns to JCheckBoxes */ private final Map mColMap = new HashMap(10); /** * Construct a PrefencesDialog with the given owner and column model. * * @param owner frame to own the dialog * @param columnModel column model to manipulate */ public PreferencesDialog(JFrame owner, MyTableColumnModel columnModel) { super(owner, "Chainsaw Preferences", true); mColModel = columnModel; final Box main = Box.createVerticalBox(); getContentPane().add(main, BorderLayout.CENTER); final JPanel filePanel = new JPanel(); filePanel.setLayout(new GridLayout(1, 2)); filePanel.add(new JLabel("Maximum # of recent files: ", JLabel.RIGHT)); filePanel.add(mMaxFilesField); // Make sure we only get digits mMaxFilesField.setDocument( new PlainDocument() { public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { if (str == null) { return; } StringBuffer realStr = new StringBuffer(str.length()); for (int i = 0; i < str.length(); i++) { if (Character.isDigit(str.charAt(i))) { realStr.append(str.charAt(i)); } } // No more than four characters if ((getLength() + realStr.length()) > 4) { int truncLen = Math.max(0, 4 - getLength()); realStr.setLength(truncLen); } super.insertString(offs, realStr.toString(), a); } }); mMaxFilesField.setText(String.valueOf(PREFS.getMaxFiles())); filePanel.setAlignmentX(1.0f); main.add(filePanel); main.add(Box.createVerticalStrut(3)); final PreferenceSet controlPrefs = PREFS.getPreferenceSet( Preferences.PROP_PREFIX, ControlPanel.PREF_SET_NAME); mSaveFilters.setSelected( controlPrefs.getBoolean(ControlPanel.SAVE_PROPERTY, true)); mSaveFilters.setHorizontalAlignment(JCheckBox.LEFT); final JPanel filterPanel = new JPanel(); filterPanel.setAlignmentX(1.0f); filterPanel.add(mSaveFilters); main.add(filterPanel); main.add(Box.createVerticalStrut(6)); final JPanel colPanel = new JPanel(); colPanel.setLayout(new GridLayout(0, 3)); final Iterator colIter = getAvailableColumns(); while (colIter.hasNext()) { final TableColumn col = (TableColumn) colIter.next(); final String colName = String.valueOf(col.getHeaderValue()); final PreferenceSet colPrefs = mColModel.getColumnPreferences(col); final boolean visible = colPrefs.getBoolean(MyTableColumnModel.COLUMN_VISIBLE_PROPERTY, true); JCheckBox check = new JCheckBox(colName, visible); mColMap.put(col, check); colPanel.add(check); } colPanel.setBorder(BorderFactory.createTitledBorder("Display columns:")); main.add(colPanel); main.add(Box.createVerticalStrut(3)); final JPanel buttonPanel = new JPanel(); buttonPanel.setAlignmentX(0.0f); buttonPanel.add(new JButton(new OKAction())); buttonPanel.add(new JButton(new CancelAction())); getContentPane().add(buttonPanel, BorderLayout.SOUTH); pack(); setLocationRelativeTo(owner); } /* Get the columns from the model in alphabetical order */ private Iterator getAvailableColumns() { SortedSet cols = new TreeSet( new Comparator() { public int compare(Object x, Object y) { final TableColumn xCol = (TableColumn) x; final TableColumn yCol = (TableColumn) y; return String.valueOf(xCol.getHeaderValue()).compareTo( String.valueOf(yCol.getHeaderValue())); } }); cols.addAll(mColModel.getAvailableColumns()); return cols.iterator(); } /** OK button handler */ private class OKAction extends AbstractAction { public OKAction() { putValue(NAME, "OK"); } public void actionPerformed(ActionEvent ae) { // File preferences try { final int maxFiles = Integer.parseInt(mMaxFilesField.getText()); PREFS.setMaxFiles(maxFiles); } catch (final NumberFormatException nfe) { // This really ought not happen given the document // unless nothing was entered } // Filter preferences final PreferenceSet controlPrefs = PREFS.getPreferenceSet( Preferences.PROP_PREFIX, ControlPanel.PREF_SET_NAME); controlPrefs.setBoolean( ControlPanel.SAVE_PROPERTY, mSaveFilters.isSelected()); // Column preferences Iterator colIter = getAvailableColumns(); while (colIter.hasNext()) { final TableColumn col = (TableColumn) colIter.next(); final JCheckBox check = (JCheckBox) mColMap.get(col); final PreferenceSet colPrefs = mColModel.getColumnPreferences(col); final boolean visible = colPrefs.getBoolean( MyTableColumnModel.COLUMN_VISIBLE_PROPERTY, true); if (check.isSelected() && !visible) { mColModel.addColumn(col); } else if (!check.isSelected() && visible) { mColModel.removeColumn(col); } } hide(); } } /** Cancel button handler */ private class CancelAction extends AbstractAction { public CancelAction() { putValue(NAME, "Cancel"); } public void actionPerformed(ActionEvent ae) { hide(); } } } 1.1 jakarta-log4j/src/java/org/apache/log4j/chainsaw/RecentFilesMenu.java Index: RecentFilesMenu.java =================================================================== /* * ============================================================================ * The Apache Software License, Version 1.1 * ============================================================================ * * Copyright (C) 1999 The Apache Software Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without modifica- * tion, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The end-user documentation included with the redistribution, if any, must * include the following acknowledgment: "This product includes software * developed by the Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, if * and wherever such third-party acknowledgments normally appear. * * 4. The names "log4j" and "Apache Software Foundation" must not be used to * endorse or promote products derived from this software without prior * written permission. For written permission, please contact * [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache", nor may * "Apache" appear in their name, without prior written permission of the * Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals * on behalf of the Apache Software Foundation. For more information on the * Apache Software Foundation, please see <http://www.apache.org/>. * */ package org.apache.log4j.chainsaw; import java.awt.event.ActionEvent; import java.io.File; import java.util.Iterator; import javax.swing.AbstractAction; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import org.apache.log4j.Logger; /** * Recent files menu. * * @author <a href="mailto:[EMAIL PROTECTED]">Raymond DeCampo</a> */ public class RecentFilesMenu extends JMenu { /** Logger for class */ private static final Logger LOG = Logger.getLogger(RecentFilesMenu.class); private final MyTableModel mModel; private final XMLFileHandler mHandler; /** * Construct a RecentFilesMenu object based on the given model. When a * file is selected from the menu, it will be loaded to the given model. * * @param model the table model */ public RecentFilesMenu(MyTableModel model) { super("Recent Files"); mModel = model; mHandler = new XMLFileHandler(model); } /** * Rebuild the menu based on the data in the * [EMAIL PROTECTED] org.apache.log4j.chainsaw.Preferences}. */ public void rebuild() { removeAll(); int order = 1; final Iterator fIter = Preferences.getInstance().getRecentFiles().iterator(); // Menu is enabled when we have files setEnabled(fIter.hasNext()); while (fIter.hasNext()) { String filename = (String) fIter.next(); JMenuItem menuItem = new JMenuItem(order + " - " + filename); menuItem.addActionListener(new LoadRecentFileAction(filename, order)); add(menuItem); if (order < 10) { menuItem.setMnemonic('0' + order); } order++; } updateUI(); } /** Handler for menu items */ private class LoadRecentFileAction extends AbstractAction { /** File to load */ private final String mFilename; public LoadRecentFileAction(String filename, int order) { mFilename = filename; putValue(NAME, order + " - " + filename); } /* Load the file */ public void actionPerformed(ActionEvent ae) { try { final File f = new File(mFilename); final int num = mHandler.loadFile(f); JOptionPane.showMessageDialog( RecentFilesMenu.this, "Loaded " + num + " events.", "CHAINSAW", JOptionPane.INFORMATION_MESSAGE); } catch (Exception e) { LOG.warn("caught an exception loading the file", e); JOptionPane.showMessageDialog( RecentFilesMenu.this, "Error parsing file - " + e.getMessage(), "CHAINSAW", JOptionPane.ERROR_MESSAGE); } } } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]