/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.log4j.chainsaw;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.table.TableColumn;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;


/**
 * GUI panel used to manipulate the PreferenceModel for a Log Panel
 *
 * @author Paul Smith
 */
public class LogPanelPreferencePanel extends AbstractPreferencePanel
{
  //~ Instance fields =========================================================

  private final LogPanelPreferenceModel preferenceModel;
  private final ModifiableListModel columnListModel = new ModifiableListModel();
  private static final Logger logger = LogManager.getLogger(LogPanelPreferencePanel.class);
  private ApplicationPreferenceModel appPreferenceModel;

  //~ Constructors ============================================================

  public LogPanelPreferencePanel(LogPanelPreferenceModel model, ApplicationPreferenceModel appModel)
  {
    preferenceModel = model;
    appPreferenceModel = appModel;
    initComponents();

    getOkButton().addActionListener(new ActionListener()
      {
        public void actionPerformed(ActionEvent e)
        {
          hidePanel();
        }
      });

    getCancelButton().addActionListener(new ActionListener()
      {
        public void actionPerformed(ActionEvent e)
        {
          hidePanel();
        }
      });
    }

  //~ Methods =================================================================

  /**
   * DOCUMENT ME!
   *
   * @param args DOCUMENT ME!
   */
  public static void main(String[] args)
  {
    JFrame f = new JFrame("Preferences Panel Test Bed");
    LogPanelPreferenceModel model = new LogPanelPreferenceModel();
    ApplicationPreferenceModel appModel = new ApplicationPreferenceModel();
    LogPanelPreferencePanel panel = new LogPanelPreferencePanel(model, appModel);
    f.getContentPane().add(panel);

    model.addPropertyChangeListener(new PropertyChangeListener()
      {
        public void propertyChange(PropertyChangeEvent evt)
        {
          logger.warn(evt.toString());
        }
      });
    panel.setOkCancelActionListener(new ActionListener()
      {
        public void actionPerformed(ActionEvent e)
        {
          System.exit(1);
        }
      });

    f.setSize(640, 480);
    f.setVisible(true);
  }

  protected TreeModel createTreeModel()
  {
    final DefaultMutableTreeNode rootNode =
      new DefaultMutableTreeNode("Preferences");
    DefaultTreeModel model = new DefaultTreeModel(rootNode);

    DefaultMutableTreeNode visuals =
      new DefaultMutableTreeNode(new VisualsPrefPanel());
    DefaultMutableTreeNode formatting =
      new DefaultMutableTreeNode(new FormattingPanel());
    DefaultMutableTreeNode columns =
      new DefaultMutableTreeNode(new ColumnSelectorPanel());

    rootNode.add(visuals);
    rootNode.add(formatting);
    rootNode.add(columns);

    return model;
  }

  //~ Inner Classes ===========================================================

  /**
   * Allows the user to choose which columns to display.
   *
   * @author Paul Smith
   *
   */
  public class ColumnSelectorPanel extends BasicPrefPanel
  {
    //~ Constructors ==========================================================

    ColumnSelectorPanel()
    {
      super("Columns");
      initComponents();
    }

    //~ Methods ===============================================================

    private void initComponents()
    {
      setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

      Box columnBox = new Box(BoxLayout.Y_AXIS);

      //		columnBox.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Displayed Columns"));
      final JList columnList = new JList();
      columnList.setVisibleRowCount(17);

      for (
        Iterator iter = preferenceModel.getColumns().iterator();
          iter.hasNext();)
      {
          TableColumn col = (TableColumn)iter.next();
          Enumeration enumeration = columnListModel.elements();
          boolean found = false;
          while (enumeration.hasMoreElements()) {
              TableColumn thisCol = (TableColumn) enumeration.nextElement();
              if (thisCol.getHeaderValue().equals(col.getHeaderValue())) {
                  found = true;
              }
          }
            if (!found) {
              columnListModel.addElement(col);
            }
      }

      columnList.setModel(columnListModel);

      CheckListCellRenderer cellRenderer = new CheckListCellRenderer()
        {
          protected boolean isSelected(Object value)
          {
            return LogPanelPreferencePanel.this.preferenceModel.isColumnVisible((TableColumn)
              value);
          }
        };

      columnList.addMouseListener(new MouseAdapter()
        {
          public void mouseClicked(MouseEvent e)
          {
              int i = columnList.locationToIndex(e.getPoint());

              if (i >= 0)
              {
                Object column = columnListModel.get(i);
                preferenceModel.toggleColumn(((TableColumn)column));
              }
          }
        });
      JButton setAsDefaultsButton = new JButton("Use selected columns as default visible columns");
      setAsDefaultsButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent actionEvent) {
          List selectedColumns = new ArrayList();
          for (int i = 0;i<columnListModel.getSize();i++) {
            if (preferenceModel.isColumnVisible((TableColumn) columnListModel.get(i))) {
              selectedColumns.add(((TableColumn)columnListModel.get(i)).getHeaderValue());
            }
          }
          appPreferenceModel.setDefaultColumnNames(selectedColumns);
        }
      });
      columnList.setCellRenderer(cellRenderer);
      columnBox.add(new JScrollPane(columnList));
      columnBox.add(Box.createVerticalStrut(5));
      columnBox.add(setAsDefaultsButton);
      add(columnBox);
      add(Box.createVerticalGlue());
    }
  }

  /**
   * Provides preference gui's for all the Formatting options
   * available for the columns etc.
   */
  private class FormattingPanel extends BasicPrefPanel
  {
    //~ Instance fields =======================================================

    private JTextField customFormatText = new JTextField("", 10);
    private JTextField loggerPrecision = new JTextField(10);
    private JRadioButton rdCustom = new JRadioButton("Custom Format ");
    private final JRadioButton rdISO =
      new JRadioButton(
        "<html><b>Fast</b> ISO 8601 format (yyyy-MM-dd HH:mm:ss) </html>");
    private final JTextField timeZone = new JTextField(10);
    private final JRadioButton rdLevelIcons = new JRadioButton("Icons ");
    private final JRadioButton rdLevelText = new JRadioButton("Text ");
    private JRadioButton rdLast;

    //~ Constructors ==========================================================

    private FormattingPanel()
    {
      super("Formatting");
      this.initComponents();
      setupListeners();
    }

    //~ Methods ===============================================================

    private void initComponents()
    {
      setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

      JPanel dateFormatPanel = new JPanel();
      dateFormatPanel.setLayout(new BoxLayout(dateFormatPanel, BoxLayout.Y_AXIS));
      dateFormatPanel.setBorder(
        BorderFactory.createTitledBorder(
          BorderFactory.createEtchedBorder(), "Timestamp"));

      ButtonGroup bgDateFormat = new ButtonGroup();

      rdISO.setSelected(preferenceModel.isUseISO8601Format());

      rdISO.setHorizontalTextPosition(SwingConstants.RIGHT);
      rdISO.setAlignmentX(Component.LEFT_ALIGNMENT);      

      bgDateFormat.add(rdISO);
      dateFormatPanel.add(rdISO);

      for (
        Iterator iter = LogPanelPreferenceModel.DATE_FORMATS.iterator();
          iter.hasNext();)
      {
        final String format = (String) iter.next();
        final JRadioButton rdFormat = new JRadioButton(format);
        rdFormat.setHorizontalTextPosition(SwingConstants.RIGHT);
        rdFormat.setAlignmentX(Component.LEFT_ALIGNMENT);      

        rdFormat.addActionListener(new ActionListener()
          {
            public void actionPerformed(ActionEvent e)
            {
              preferenceModel.setDateFormatPattern(format);
              customFormatText.setEnabled(rdCustom.isSelected());
              rdLast = rdFormat;
            }
          });
        //update based on external changes to dateformatpattern (column context
        //menu)
        preferenceModel.addPropertyChangeListener(
          "dateFormatPattern", new PropertyChangeListener()
          {
            public void propertyChange(PropertyChangeEvent evt)
            {
              rdFormat.setSelected(
                preferenceModel.getDateFormatPattern().equals(format));
              rdLast = rdFormat;
            }
          });

        dateFormatPanel.add(rdFormat);
        bgDateFormat.add(rdFormat);
      }

      customFormatText.setPreferredSize(new Dimension(100, 20));
      customFormatText.setMaximumSize(customFormatText.getPreferredSize());
      customFormatText.setMinimumSize(customFormatText.getPreferredSize());
      customFormatText.setEnabled(false);

      bgDateFormat.add(rdCustom);
      rdCustom.setSelected(preferenceModel.isCustomDateFormat());

      // add a custom date format
      if (preferenceModel.isCustomDateFormat())
      {
        customFormatText.setText(preferenceModel.getDateFormatPattern());
        customFormatText.setEnabled(true);
      }

      JPanel customPanel = new JPanel();
      customPanel.setLayout(new BoxLayout(customPanel, BoxLayout.X_AXIS));
      customPanel.add(rdCustom);
      customPanel.add(customFormatText);
      customPanel.setAlignmentX(Component.LEFT_ALIGNMENT);      

      dateFormatPanel.add(customPanel);
      dateFormatPanel.add(Box.createVerticalStrut(5));

      JLabel dateFormatLabel = new JLabel("Time zone of events (or blank for local time zone");
      dateFormatPanel.add(dateFormatLabel);

      timeZone.setMaximumSize(timeZone.getPreferredSize());
      dateFormatPanel.add(Box.createVerticalStrut(5));
      dateFormatPanel.add(timeZone);
      
      add(dateFormatPanel);

      JPanel levelFormatPanel = new JPanel();
      levelFormatPanel.setLayout(
        new BoxLayout(levelFormatPanel, BoxLayout.Y_AXIS));
      levelFormatPanel.setBorder(
        BorderFactory.createTitledBorder(
          BorderFactory.createEtchedBorder(), "Level"));
      levelFormatPanel.setAlignmentX(Component.LEFT_ALIGNMENT);

      ButtonGroup bgLevel = new ButtonGroup();
      bgLevel.add(rdLevelIcons);
      bgLevel.add(rdLevelText);

      rdLevelIcons.setSelected(preferenceModel.isLevelIcons());

      levelFormatPanel.add(rdLevelIcons);
      levelFormatPanel.add(rdLevelText);

      add(levelFormatPanel);

      JPanel loggerFormatPanel = new JPanel();
      loggerFormatPanel.setLayout(
        new BoxLayout(loggerFormatPanel, BoxLayout.Y_AXIS));
      loggerFormatPanel.setBorder(
        BorderFactory.createTitledBorder(
          BorderFactory.createEtchedBorder(), "Logger"));
      loggerFormatPanel.setAlignmentX(Component.LEFT_ALIGNMENT);

      loggerFormatPanel.add(Box.createVerticalStrut(3));

      final JLabel precisionLabel =
        new JLabel("Number of package levels to hide (or blank to display full logger)");

      loggerFormatPanel.add(precisionLabel);
      loggerFormatPanel.add(Box.createVerticalStrut(5));

      loggerPrecision.setMaximumSize(loggerPrecision.getPreferredSize());
      loggerFormatPanel.add(loggerPrecision);

      add(loggerFormatPanel);
    }

    /*
     * Restore text fields to current model values
     */
    private void reset() {

    	if (preferenceModel.isCustomDateFormat()) {
    		customFormatText.setText(preferenceModel.getDateFormatPattern());
    	} else {
    		if (rdLast != null) {
    			rdLast.setSelected(true);
    		}
    		customFormatText.setEnabled(false);
    	}
    	
    	loggerPrecision.setText(preferenceModel.getLoggerPrecision());
    	timeZone.setText(preferenceModel.getTimeZone());
    }

    /*
     * Commit text fields to model
     */
    private void commit() {
    	if (rdCustom.isSelected()) {
    		preferenceModel.setDateFormatPattern(customFormatText.getText());
    	}
    	preferenceModel.setLoggerPrecision(loggerPrecision.getText());
    	preferenceModel.setTimeZone(timeZone.getText());
    }

    /**
     * DOCUMENT ME!
    */
    private void setupListeners()
    {
      getOkButton().addActionListener(new ActionListener() {
    	  public void actionPerformed(ActionEvent evt) {
    		  commit();
    	  }
      });
      
      getCancelButton().addActionListener(new ActionListener() {
    	  public void actionPerformed(ActionEvent evt) {
    		  reset();
    	  }
      });
    	  
    	  rdCustom.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent e)
          {
            customFormatText.setEnabled(rdCustom.isSelected());
            customFormatText.grabFocus();
          }
        });

      //a second?? listener for dateformatpattern
      preferenceModel.addPropertyChangeListener(
        "dateFormatPattern", new PropertyChangeListener()
        {
          public void propertyChange(PropertyChangeEvent evt)
          {
            /**
             * we need to make sure we are not reacting to the user typing, so only do this
             * if the text box is not the same as the model
             */
            if (
              preferenceModel.isCustomDateFormat()
                && !customFormatText.getText().equals(
                  evt.getNewValue().toString()))
            {
              customFormatText.setText(preferenceModel.getDateFormatPattern());
              rdCustom.setSelected(true);
              customFormatText.setEnabled(true);
            }
            else
            {
              rdCustom.setSelected(false);
            }
          }
        });

      rdISO.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent e)
          {
            preferenceModel.setDateFormatPattern("ISO8601");
            customFormatText.setEnabled(rdCustom.isSelected());
            rdLast = rdISO;
          }
        });
      preferenceModel.addPropertyChangeListener(
        "dateFormatPattern", new PropertyChangeListener()
        {
          public void propertyChange(PropertyChangeEvent evt)
          {
            rdISO.setSelected(preferenceModel.isUseISO8601Format());
            rdLast = rdISO;
          }
        });
      preferenceModel.addPropertyChangeListener(
          "dateFormatTimeZone", new PropertyChangeListener() {
              public void propertyChange(PropertyChangeEvent evt) {
                  timeZone.setText(preferenceModel.getTimeZone());
              }
          }
      );

      ActionListener levelIconListener = new ActionListener()
        {
          public void actionPerformed(ActionEvent e)
          {
            preferenceModel.setLevelIcons(rdLevelIcons.isSelected());
          }
        };

      rdLevelIcons.addActionListener(levelIconListener);
      rdLevelText.addActionListener(levelIconListener);

      preferenceModel.addPropertyChangeListener(
        "levelIcons", new PropertyChangeListener()
        {
          public void propertyChange(PropertyChangeEvent evt)
          {
            boolean value = ((Boolean) evt.getNewValue()).booleanValue();
            rdLevelIcons.setSelected(value);
            rdLevelText.setSelected(!value);
          }
        });
    }
  }

  /**
   * DOCUMENT ME!
   *
   * @author $author$
   * @version $Revision$, $Date$
   *
   * @author psmith
   *
   */
  private class VisualsPrefPanel extends BasicPrefPanel
  {
    //~ Instance fields =======================================================

    private final JCheckBox detailPanelVisible =
      new JCheckBox("Show Event Detail panel");

    private final JCheckBox loggerTreePanel =
      new JCheckBox("Show Logger Tree");
    private final JCheckBox wrapMessage = new JCheckBox("Wrap message field (display multi-line rows) ");
    private final JCheckBox searchResultsVisible = new JCheckBox("Display find results in details panel ");
    private final JCheckBox highlightSearchMatchText = new JCheckBox("Highlight find match text ");
    private final JCheckBox scrollToBottom =
      new JCheckBox("Scroll to bottom (view tracks with new events)");
    private final JCheckBox showMillisDeltaAsGap =
      new JCheckBox("Display timestamp delta between events as row gap");
    private final JCheckBox toolTips =
      new JCheckBox("Show Event Detail Tooltips");
    private final JCheckBox thumbnailBarToolTips =
      new JCheckBox("Show Thumbnail Bar Tooltips");
    private final JEditorPane clearTableExpression = new JEditorPane();

    //~ Constructors ==========================================================

    /**
     * Creates a new VisualsPrefPanel object.
    */
    private VisualsPrefPanel()
    {
      super("Visuals");
      initPanelComponents();
      setupListeners();
    }

    //~ Methods ===============================================================

    /**
     * DOCUMENT ME!
    */
    private void initPanelComponents()
    {
      JTextComponentFormatter.applySystemFontAndSize(clearTableExpression);
      setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
      toolTips.setAlignmentX(Component.LEFT_ALIGNMENT);
      thumbnailBarToolTips.setAlignmentX(Component.LEFT_ALIGNMENT);
      detailPanelVisible.setAlignmentX(Component.LEFT_ALIGNMENT);
      loggerTreePanel.setAlignmentX(Component.LEFT_ALIGNMENT);
      scrollToBottom.setAlignmentX(Component.LEFT_ALIGNMENT);
      showMillisDeltaAsGap.setAlignmentX(Component.LEFT_ALIGNMENT);
      add(toolTips);
      add(thumbnailBarToolTips);
      add(detailPanelVisible);
      add(loggerTreePanel);
      add(scrollToBottom);
      add(wrapMessage);
      add(highlightSearchMatchText);
      add(searchResultsVisible);
      add(showMillisDeltaAsGap);
      JPanel clearPanel = new JPanel(new BorderLayout());
      clearPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
      clearPanel.add(new JLabel("Clear all events if expression matches"), BorderLayout.NORTH);
      clearTableExpression.setText(preferenceModel.getClearTableExpression());
      clearTableExpression.setPreferredSize(new Dimension(300, 50));
      JPanel clearTableScrollPanel = new JPanel(new BorderLayout());
      clearTableScrollPanel.add(new JScrollPane(clearTableExpression), BorderLayout.NORTH);
      clearPanel.add(clearTableScrollPanel, BorderLayout.CENTER);
      add(clearPanel);

      toolTips.setSelected(preferenceModel.isToolTips());
      thumbnailBarToolTips.setSelected(preferenceModel.isThumbnailBarToolTips());
      detailPanelVisible.setSelected(preferenceModel.isDetailPaneVisible());
      searchResultsVisible.setSelected(preferenceModel.isSearchResultsVisible());
      loggerTreePanel.setSelected(preferenceModel.isLogTreePanelVisible());
    }

    /**
     * DOCUMENT ME!
    */
    private void setupListeners()
    {
        ActionListener wrapMessageListener = new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                preferenceModel.setWrapMessage(wrapMessage.isSelected());
            }
        };

        wrapMessage.addActionListener(wrapMessageListener);

        ActionListener searchResultsVisibleListener = new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                preferenceModel.setSearchResultsVisible(searchResultsVisible.isSelected());
            }
        };

        searchResultsVisible.addActionListener(searchResultsVisibleListener);

        ActionListener highlightSearchMatchTextListener = new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                preferenceModel.setHighlightSearchMatchText(highlightSearchMatchText.isSelected());
            }
        };

        highlightSearchMatchText.addActionListener(highlightSearchMatchTextListener);

        preferenceModel.addPropertyChangeListener(
          "wrapMessage", new PropertyChangeListener()
          {
            public void propertyChange(PropertyChangeEvent evt)
            {
              boolean value = ((Boolean) evt.getNewValue()).booleanValue();
              wrapMessage.setSelected(value);
            }
          });

      preferenceModel.addPropertyChangeListener(
        "searchResultsVisible", new PropertyChangeListener()
        {
          public void propertyChange(PropertyChangeEvent evt)
          {
            boolean value = ((Boolean) evt.getNewValue()).booleanValue();
            searchResultsVisible.setSelected(value);
          }
        });

        preferenceModel.addPropertyChangeListener(
          "highlightSearchMatchText", new PropertyChangeListener()
          {
            public void propertyChange(PropertyChangeEvent evt)
            {
              boolean value = ((Boolean) evt.getNewValue()).booleanValue();
              highlightSearchMatchText.setSelected(value);
            }
          });

      toolTips.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent e)
          {
            preferenceModel.setToolTips(toolTips.isSelected());
          }
        });

      thumbnailBarToolTips.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent e) {
              preferenceModel.setThumbnailBarToolTips(thumbnailBarToolTips.isSelected());
          }
      });

      getOkButton().addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent e) {
              preferenceModel.setClearTableExpression(clearTableExpression.getText().trim());
          }
      });

      preferenceModel.addPropertyChangeListener(
        "toolTips", new PropertyChangeListener()
        {
          public void propertyChange(PropertyChangeEvent evt)
          {
            boolean value = ((Boolean) evt.getNewValue()).booleanValue();
            toolTips.setSelected(value);
          }
        });

    preferenceModel.addPropertyChangeListener(
      "thumbnailBarToolTips", new PropertyChangeListener()
      {
        public void propertyChange(PropertyChangeEvent evt)
        {
          boolean value = ((Boolean) evt.getNewValue()).booleanValue();
          thumbnailBarToolTips.setSelected(value);
        }
      });

      detailPanelVisible.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent e)
          {
            preferenceModel.setDetailPaneVisible(detailPanelVisible.isSelected());
          }
        });

      preferenceModel.addPropertyChangeListener(
        "detailPaneVisible", new PropertyChangeListener()
        {
          public void propertyChange(PropertyChangeEvent evt)
          {
            boolean value = ((Boolean) evt.getNewValue()).booleanValue();
            detailPanelVisible.setSelected(value);
          }
        });

      scrollToBottom.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent e)
          {
            preferenceModel.setScrollToBottom(scrollToBottom.isSelected());
          }
        });

      showMillisDeltaAsGap.addActionListener(new ActionListener()
      {
          public void actionPerformed(ActionEvent e)
          {
              preferenceModel.setShowMillisDeltaAsGap(showMillisDeltaAsGap.isSelected());
          }
      });

      preferenceModel.addPropertyChangeListener("showMillisDeltaAsGap", new PropertyChangeListener()
      {
          public void propertyChange(PropertyChangeEvent evt)
          {
              boolean value = ((Boolean) evt.getNewValue()).booleanValue();
              showMillisDeltaAsGap.setSelected(value);
          }
      });
      preferenceModel.addPropertyChangeListener(
        "scrollToBottom", new PropertyChangeListener()
        {
          public void propertyChange(PropertyChangeEvent evt)
          {
            boolean value = ((Boolean) evt.getNewValue()).booleanValue();
            scrollToBottom.setSelected(value);
          }
        });

      loggerTreePanel.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent e)
          {
            preferenceModel.setLogTreePanelVisible(loggerTreePanel.isSelected());
          }
        });

      preferenceModel.addPropertyChangeListener(
        "logTreePanelVisible", new PropertyChangeListener()
        {
          public void propertyChange(PropertyChangeEvent evt)
          {
            boolean value = ((Boolean) evt.getNewValue()).booleanValue();
            loggerTreePanel.setSelected(value);
          }
        });

      preferenceModel.addPropertyChangeListener("columns", new PropertyChangeListener() {
        	public void propertyChange(PropertyChangeEvent evt) {
      	  List cols = (List)evt.getNewValue();
            for (
          	        Iterator iter = cols.iterator();
          	          iter.hasNext();)
          	      {
          	        TableColumn col = (TableColumn) iter.next();
                    Enumeration enumeration = columnListModel.elements();
                    boolean found = false;
                    while (enumeration.hasMoreElements()) {
                        TableColumn thisCol = (TableColumn) enumeration.nextElement();
                        if (thisCol.getHeaderValue().equals(col.getHeaderValue())) {
                            found = true;
                        }
                    }
          	        if (!found) {
          	        	columnListModel.addElement(col);
          	            columnListModel.fireContentsChanged();
          	        }
          	      }
        }});

        preferenceModel.addPropertyChangeListener(
                "visibleColumns", new PropertyChangeListener()
                {
                  public void propertyChange(PropertyChangeEvent evt)
                  {
                    columnListModel.fireContentsChanged();
                  }
                });

        preferenceModel.addPropertyChangeListener("clearTableExpression", new PropertyChangeListener()
        {
            public void propertyChange(PropertyChangeEvent evt) {
                clearTableExpression.setText(((LogPanelPreferenceModel)evt.getSource()).getClearTableExpression());
            }
        });
    }
  }
}
