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 &quot;org.apache.log4j.chainsaw&quot;
     *  and the name is &quot;table&quot; the full prefix is
     *  &quot;org.apache.log4j.chainsaw.table&quot;.  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 &quot;user.home&quot;.
   *  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]

Reply via email to