Added ability to save the receiver configuration defined through the initial receiver configuration panel with a user-specified file path and name
Project: http://git-wip-us.apache.org/repos/asf/logging-chainsaw/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-chainsaw/commit/f95619e9 Tree: http://git-wip-us.apache.org/repos/asf/logging-chainsaw/tree/f95619e9 Diff: http://git-wip-us.apache.org/repos/asf/logging-chainsaw/diff/f95619e9 Branch: refs/heads/master Commit: f95619e914469204882877d812354a4fc74884ee Parents: 345dc27 Author: Scott Deboy <[email protected]> Authored: Thu Nov 4 08:12:54 2010 +0000 Committer: Scott Deboy <[email protected]> Committed: Thu Nov 4 08:12:54 2010 +0000 ---------------------------------------------------------------------- .../ApplicationPreferenceModelPanel.java | 2 +- .../apache/log4j/chainsaw/FileLoadAction.java | 2 +- .../java/org/apache/log4j/chainsaw/LogUI.java | 24 +++-- .../chainsaw/ReceiverConfigurationPanel.java | 60 +++++++++--- .../log4j/chainsaw/helper/SwingHelper.java | 11 ++- .../chainsaw/receivers/ReceiversHelper.java | 99 +++++++++++++++++++- .../chainsaw/receivers/ReceiversPanel.java | 90 +----------------- .../log4j/chainsaw/help/release-notes.html | 1 + 8 files changed, 175 insertions(+), 114 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/f95619e9/src/main/java/org/apache/log4j/chainsaw/ApplicationPreferenceModelPanel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/chainsaw/ApplicationPreferenceModelPanel.java b/src/main/java/org/apache/log4j/chainsaw/ApplicationPreferenceModelPanel.java index 64cba35..c1460e0 100644 --- a/src/main/java/org/apache/log4j/chainsaw/ApplicationPreferenceModelPanel.java +++ b/src/main/java/org/apache/log4j/chainsaw/ApplicationPreferenceModelPanel.java @@ -560,7 +560,7 @@ public static void main(String[] args) { } } } - File selectedFile = SwingHelper.promptForFile(this, defaultPath, "Select a Chainsaw configuration file"); + File selectedFile = SwingHelper.promptForFile(this, defaultPath, "Select a Chainsaw configuration file", true); if (selectedFile != null) { try { http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/f95619e9/src/main/java/org/apache/log4j/chainsaw/FileLoadAction.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/chainsaw/FileLoadAction.java b/src/main/java/org/apache/log4j/chainsaw/FileLoadAction.java index 638bc50..44b16cd 100644 --- a/src/main/java/org/apache/log4j/chainsaw/FileLoadAction.java +++ b/src/main/java/org/apache/log4j/chainsaw/FileLoadAction.java @@ -80,7 +80,7 @@ class FileLoadAction extends AbstractAction { if (!remoteURL) { try { - File selectedFile = SwingHelper.promptForFile(parent, null, "Load Events from XML file or zipped XML file..."); + File selectedFile = SwingHelper.promptForFile(parent, null, "Load Events from XML file or zipped XML file...", true); if (selectedFile != null) { url = selectedFile.toURI().toURL(); name = selectedFile.getName(); http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/f95619e9/src/main/java/org/apache/log4j/chainsaw/LogUI.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/chainsaw/LogUI.java b/src/main/java/org/apache/log4j/chainsaw/LogUI.java index 12502f3..dd9775a 100644 --- a/src/main/java/org/apache/log4j/chainsaw/LogUI.java +++ b/src/main/java/org/apache/log4j/chainsaw/LogUI.java @@ -106,6 +106,7 @@ import org.apache.log4j.chainsaw.prefs.MRUFileListPreferenceSaver; import org.apache.log4j.chainsaw.prefs.SaveSettingsEvent; import org.apache.log4j.chainsaw.prefs.SettingsListener; import org.apache.log4j.chainsaw.prefs.SettingsManager; +import org.apache.log4j.chainsaw.receivers.ReceiversHelper; import org.apache.log4j.chainsaw.receivers.ReceiversPanel; import org.apache.log4j.chainsaw.vfs.VFSLogFilePatternReceiver; import org.apache.log4j.net.SocketNodeEventListener; @@ -1418,16 +1419,17 @@ public class LogUI extends JFrame implements ChainsawViewer, SettingsListener { public void actionPerformed(ActionEvent e) { dialog.setVisible(false); + if (receiverConfigurationPanel.getModel().isCancelled()) { + return; + } applicationPreferenceModel.setShowNoReceiverWarning(!receiverConfigurationPanel.isDontWarnMeAgain()); - //using this config next time - stop all plugins - if (receiverConfigurationPanel.isDontWarnMeAgain()) { - List plugins = pluginRegistry.getPlugins(); - for (Iterator iter = plugins.iterator();iter.hasNext();) { - Plugin plugin = (Plugin)iter.next(); - //don't stop ZeroConfPlugin if it is registered - if (!plugin.getName().toLowerCase().contains("zeroconf")) { - pluginRegistry.stopPlugin(plugin.getName()); - } + //remove existing plugins + List plugins = pluginRegistry.getPlugins(); + for (Iterator iter = plugins.iterator();iter.hasNext();) { + Plugin plugin = (Plugin)iter.next(); + //don't stop ZeroConfPlugin if it is registered + if (!plugin.getName().toLowerCase().contains("zeroconf")) { + pluginRegistry.stopPlugin(plugin.getName()); } } URL configURL = null; @@ -1525,6 +1527,10 @@ public class LogUI extends JFrame implements ChainsawViewer, SettingsListener { } }).start(); } + File saveConfigFile = receiverConfigurationPanel.getModel().getSaveConfigFile(); + if (saveConfigFile != null) { + ReceiversHelper.getInstance().saveReceiverConfiguration(saveConfigFile); + } } }); http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/f95619e9/src/main/java/org/apache/log4j/chainsaw/ReceiverConfigurationPanel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/chainsaw/ReceiverConfigurationPanel.java b/src/main/java/org/apache/log4j/chainsaw/ReceiverConfigurationPanel.java index 8265361..d04a8e1 100644 --- a/src/main/java/org/apache/log4j/chainsaw/ReceiverConfigurationPanel.java +++ b/src/main/java/org/apache/log4j/chainsaw/ReceiverConfigurationPanel.java @@ -46,7 +46,6 @@ import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JTextField; import javax.swing.JTextPane; -import javax.swing.SwingUtilities; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.StyledDocument; @@ -93,6 +92,7 @@ class ReceiverConfigurationPanel extends JPanel { //don't warn again widgets private JCheckBox dontwarnIfNoReceiver; + private JButton saveButton; private JButton okButton; private JButton cancelButton; @@ -214,6 +214,14 @@ class ReceiverConfigurationPanel extends JPanel { dontwarnIfNoReceiver = new JCheckBox("Always start Chainsaw with this configuration"); panel.add(dontwarnIfNoReceiver, c); + saveButton = new JButton(" Save configuration as... "); + c = new GridBagConstraints(); + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 1; + c.gridy = 0; + c.insets = new Insets(0, 0, 0, 10); + panel.add(saveButton, c); + okButton = new JButton(" OK "); cancelButton = new JButton(" Cancel "); @@ -221,14 +229,14 @@ class ReceiverConfigurationPanel extends JPanel { c = new GridBagConstraints(); c.fill = GridBagConstraints.HORIZONTAL; - c.gridx = 1; + c.gridx = 2; c.gridy = 0; c.insets = new Insets(0, 0, 0, 10); panel.add((JButton)okCancelButtons.get(0), c); c = new GridBagConstraints(); c.fill = GridBagConstraints.HORIZONTAL; - c.gridx = 2; + c.gridx = 3; c.gridy = 0; panel.add((JButton)okCancelButtons.get(1), c); @@ -249,10 +257,27 @@ class ReceiverConfigurationPanel extends JPanel { if (logFileFormatComboBox.getSelectedItem() != null && !(logFileFormatComboBox.getSelectedItem().toString().trim().equals(""))) { panelModel.setLogFormat(logFileFormatComboBox.getSelectedItem().toString()); } - completionActionListener.actionPerformed(new ActionEvent(this, -1, "cancelled")); + completionActionListener.actionPerformed(new ActionEvent(this, -1, "ok")); } }); - return panel; + + saveButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + try { + URL url = browseFile("Choose a path and file name to save", false); + if (url != null) { + File file = new File(url.toURI()); + panelModel.setSaveConfigFile(file); + } + } catch (Exception ex) { + logger.error( + "Error browsing for log file", ex); + } + } + }); + return panel; } private JPanel buildBottomDescriptionPanel() { @@ -323,8 +348,7 @@ class ReceiverConfigurationPanel extends JPanel { browseLogFileButton = new JButton(new AbstractAction(" Open File... ") { public void actionPerformed(ActionEvent e) { try { - - URL url = browseLogFile(); + URL url = browseFile("Select a log file", true); if (url != null) { String item = url.toURI().toString(); logFileURLTextField.setText(item); @@ -564,7 +588,7 @@ class ReceiverConfigurationPanel extends JPanel { private URL browseConfig() throws MalformedURLException { //hiding and showing the dialog to avoid focus issues with 2 dialogs dialog.setVisible(false); - File selectedFile = SwingHelper.promptForFile(dialog, null, "Choose a Chainsaw configuration file"); + File selectedFile = SwingHelper.promptForFile(dialog, null, "Choose a Chainsaw configuration file", true); URL result = null; if (selectedFile == null) { result = null; @@ -587,10 +611,10 @@ class ReceiverConfigurationPanel extends JPanel { * Returns the URL chosen by the user for a Configuration file * or null if they cancelled. */ - private URL browseLogFile() throws MalformedURLException { + private URL browseFile(String title, boolean loadDialog) throws MalformedURLException { //hiding and showing the dialog to avoid focus issues with 2 dialogs dialog.setVisible(false); - File selectedFile = SwingHelper.promptForFile(dialog, null, "Select a log file"); + File selectedFile = SwingHelper.promptForFile(dialog, null, title, loadDialog); URL result = null; if (selectedFile == null) { result = null; @@ -641,6 +665,7 @@ class ReceiverConfigurationPanel extends JPanel { //default to cancelled private boolean cancelled = true; private String lastLogFormat; + private File saveConfigFile; public PanelModel(){ file = new File(SettingsManager.getInstance().getSettingsDirectory(), "receiver-config.xml"); @@ -666,7 +691,6 @@ class ReceiverConfigurationPanel extends JPanel { } boolean isLoadSavedConfigs() { - return !cancelled && useAutoSavedConfigRadioButton.isSelected(); } @@ -695,6 +719,14 @@ class ReceiverConfigurationPanel extends JPanel { return null; } + File getSaveConfigFile() { + return saveConfigFile; + } + + void setSaveConfigFile(File file) { + this.saveConfigFile = file; + } + URL getLogFileURL() { try { @@ -746,5 +778,9 @@ class ReceiverConfigurationPanel extends JPanel { logFileFormatComboBoxModel.insertElementAt(lastLogFormat, 0); logFileFormatComboBox.setSelectedIndex(0); } - } + + public boolean isCancelled() { + return cancelled; + } + } } http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/f95619e9/src/main/java/org/apache/log4j/chainsaw/helper/SwingHelper.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/chainsaw/helper/SwingHelper.java b/src/main/java/org/apache/log4j/chainsaw/helper/SwingHelper.java index 89045a6..239a8bc 100644 --- a/src/main/java/org/apache/log4j/chainsaw/helper/SwingHelper.java +++ b/src/main/java/org/apache/log4j/chainsaw/helper/SwingHelper.java @@ -107,7 +107,7 @@ public final class SwingHelper { return result; } - public static File promptForFile(Container parent, String defaultPath, String title) { + public static File promptForFile(Container parent, String defaultPath, String title, boolean loadDialog) { if (SwingHelper.isMacOSX()) { //use filedialog on mac Component root = SwingUtilities.getRoot(parent); @@ -118,6 +118,7 @@ public final class SwingHelper { FileDialog fileDialog = new FileDialog(frame, title); fileDialog.setModal(true); + fileDialog.setMode(loadDialog ? FileDialog.LOAD : FileDialog.SAVE); if (defaultPath != null) { fileDialog.setDirectory(defaultPath); } @@ -144,7 +145,13 @@ public final class SwingHelper { chooser.setAcceptAllFileFilterUsed(true); - int i = chooser.showOpenDialog(parent); + int i; + if (loadDialog) { + i = chooser.showOpenDialog(parent); + } else { + i = chooser.showSaveDialog(parent); + } + if (i != JFileChooser.APPROVE_OPTION) { return null; } http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/f95619e9/src/main/java/org/apache/log4j/chainsaw/receivers/ReceiversHelper.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/chainsaw/receivers/ReceiversHelper.java b/src/main/java/org/apache/log4j/chainsaw/receivers/ReceiversHelper.java index 2db5d16..1c7a671 100644 --- a/src/main/java/org/apache/log4j/chainsaw/receivers/ReceiversHelper.java +++ b/src/main/java/org/apache/log4j/chainsaw/receivers/ReceiversHelper.java @@ -17,17 +17,39 @@ package org.apache.log4j.chainsaw.receivers; +import java.beans.BeanInfo; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Iterator; import java.util.List; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.apache.log4j.chainsaw.plugins.PluginClassLoaderFactory; +import org.apache.log4j.plugins.Plugin; +import org.apache.log4j.plugins.PluginRegistry; +import org.apache.log4j.plugins.Receiver; +import org.apache.log4j.spi.LoggerRepository; +import org.apache.log4j.spi.LoggerRepositoryEx; +import org.w3c.dom.Document; +import org.w3c.dom.Element; /** @@ -108,6 +130,81 @@ public class ReceiversHelper { public List getKnownReceiverClasses() { return Collections.unmodifiableList(receiverClassList); } - + + public void saveReceiverConfiguration(File file) { + LoggerRepository repo = LogManager.getLoggerRepository(); + PluginRegistry pluginRegistry = ((LoggerRepositoryEx) repo).getPluginRegistry(); + List fullPluginList = pluginRegistry.getPlugins(); + List pluginList = new ArrayList(); + for (Iterator iter = fullPluginList.iterator();iter.hasNext();) { + Plugin thisPlugin = (Plugin)iter.next(); + if (thisPlugin instanceof Receiver) { + pluginList.add(thisPlugin); + } + } + //remove everything that isn't a receiver..otherwise, we'd create an empty config file + try { + if (pluginList.size() > 0) { + //we programmatically register the ZeroConf plugin in the plugin registry + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.newDocument(); + Element rootElement = document.createElementNS("http://jakarta.apache.org/log4j/", "configuration"); + rootElement.setPrefix("log4j"); + rootElement.setAttribute("xmlns:log4j", "http://jakarta.apache.org/log4j/"); + rootElement.setAttribute("debug", "true"); + + for (int i = 0; i < pluginList.size(); i++) { + Receiver receiver; + + if (pluginList.get(i) instanceof Receiver) { + receiver = (Receiver) pluginList.get(i); + } else { + continue; + } + + Element pluginElement = document.createElement("plugin"); + pluginElement.setAttribute("name", receiver.getName()); + pluginElement.setAttribute("class", receiver.getClass().getName()); + + BeanInfo beanInfo = Introspector.getBeanInfo(receiver.getClass()); + List list = new ArrayList(Arrays.asList(beanInfo.getPropertyDescriptors())); + + for (int j = 0; j < list.size(); j++) { + PropertyDescriptor d = (PropertyDescriptor) list.get(j); + //don't serialize the loggerRepository property for subclasses of componentbase.. + //easier to change this than tweak componentbase right now.. + if (d.getReadMethod().getName().equals("getLoggerRepository")) { + continue; + } + Object o = d.getReadMethod().invoke(receiver, new Object[] {} ); + if (o != null) { + Element paramElement = document.createElement("param"); + paramElement.setAttribute("name", d.getName()); + paramElement.setAttribute("value", o.toString()); + pluginElement.appendChild(paramElement); + } + } + + rootElement.appendChild(pluginElement); + + } + + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); + DOMSource source = new DOMSource(rootElement); + FileOutputStream stream = new FileOutputStream(file); + StreamResult result = new StreamResult(stream); + transformer.transform(source, result); + stream.close(); + } + + } catch (Exception e) { + e.printStackTrace(); + } + } } http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/f95619e9/src/main/java/org/apache/log4j/chainsaw/receivers/ReceiversPanel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/chainsaw/receivers/ReceiversPanel.java b/src/main/java/org/apache/log4j/chainsaw/receivers/ReceiversPanel.java index d20037f..91ba7ca 100644 --- a/src/main/java/org/apache/log4j/chainsaw/receivers/ReceiversPanel.java +++ b/src/main/java/org/apache/log4j/chainsaw/receivers/ReceiversPanel.java @@ -22,17 +22,12 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.beans.BeanInfo; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; import java.io.File; -import java.io.FileOutputStream; import javax.swing.AbstractAction; import javax.swing.Action; @@ -62,13 +57,6 @@ import javax.swing.event.TreeWillExpandListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.ExpandVetoException; import javax.swing.tree.TreePath; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.Transformer; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.dom.DOMSource; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.DocumentBuilder; import org.apache.log4j.Level; import org.apache.log4j.LogManager; @@ -95,8 +83,6 @@ import org.apache.log4j.plugins.PluginRegistry; import org.apache.log4j.plugins.Receiver; import org.apache.log4j.spi.LoggerRepository; import org.apache.log4j.spi.LoggerRepositoryEx; -import org.w3c.dom.Element; -import org.w3c.dom.Document; /** @@ -642,80 +628,8 @@ public class ReceiversPanel extends JPanel implements SettingsListener { */ public void saveSettings(SaveSettingsEvent event){ - List fullPluginList = pluginRegistry.getPlugins(); - List pluginList = new ArrayList(); - for (Iterator iter = fullPluginList.iterator();iter.hasNext();) { - Plugin thisPlugin = (Plugin)iter.next(); - if (thisPlugin instanceof Receiver) { - pluginList.add(thisPlugin); - } - } - //remove everything that isn't a receiver..otherwise, we'd create an empty config file - try { - if (pluginList.size() > 0) { - //we programmatically register the ZeroConf plugin in the plugin registry - File file = new File(SettingsManager.getInstance().getSettingsDirectory(), "receiver-config.xml"); - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document document = builder.newDocument(); - Element rootElement = document.createElementNS("http://jakarta.apache.org/log4j/", "configuration"); - rootElement.setPrefix("log4j"); - rootElement.setAttribute("xmlns:log4j", "http://jakarta.apache.org/log4j/"); - rootElement.setAttribute("debug", "true"); - - for (int i = 0; i < pluginList.size(); i++) { - Receiver receiver; - - if (pluginList.get(i) instanceof Receiver) { - receiver = (Receiver) pluginList.get(i); - } else { - continue; - } - - Element pluginElement = document.createElement("plugin"); - pluginElement.setAttribute("name", receiver.getName()); - pluginElement.setAttribute("class", receiver.getClass().getName()); - - BeanInfo beanInfo = Introspector.getBeanInfo(receiver.getClass()); - List list = new ArrayList(Arrays.asList(beanInfo.getPropertyDescriptors())); - - for (int j = 0; j < list.size(); j++) { - PropertyDescriptor d = (PropertyDescriptor) list.get(j); - //don't serialize the loggerRepository property for subclasses of componentbase.. - //easier to change this than tweak componentbase right now.. - if (d.getReadMethod().getName().equals("getLoggerRepository")) { - continue; - } - Object o = d.getReadMethod().invoke(receiver, new Object[] {} ); - if (o != null) { - Element paramElement = document.createElement("param"); - paramElement.setAttribute("name", d.getName()); - paramElement.setAttribute("value", o.toString()); - pluginElement.appendChild(paramElement); - } - } - - rootElement.appendChild(pluginElement); - - } - - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - Transformer transformer = transformerFactory.newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); - DOMSource source = new DOMSource(rootElement); - FileOutputStream stream = new FileOutputStream(file); - StreamResult result = new StreamResult(stream); - transformer.transform(source, result); - stream.close(); - } - - } catch (Exception e) { - e.printStackTrace(); - logger.error("Error while writing receiver configurations to the configuration file"); - } - + File file = new File(SettingsManager.getInstance().getSettingsDirectory(), "receiver-config.xml"); + ReceiversHelper.getInstance().saveReceiverConfiguration(file); } /** http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/f95619e9/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html ---------------------------------------------------------------------- diff --git a/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html b/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html index 3068296..803563d 100644 --- a/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html +++ b/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html @@ -12,6 +12,7 @@ <h1>2.1</h1> <h2>4 Nov 2010</h2> <ul> +<li>Added ability to save the receiver configuration defined through the initial receiver configuration panel with a user-specified file path and name</li> <li>Added ability to define which columns are displayed by default when a new tab is created by clicking the 'Use selected columns as default visible columns' button on the logpanel preferences column selection screen</li> </ul> <h2>22 Oct 2010</h2>
