net.grinder.console.swingui.ConsoleUI Maven / Gradle / Ivy
// Copyright (C) 2000 - 2012 Philip Aston
// All rights reserved.
//
// This file is part of The Grinder software distribution. Refer to
// the file LICENSE which is part of The Grinder distribution for
// licensing details. The Grinder distribution is available on the
// Internet at http://grinder.sourceforge.net/
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS 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
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, 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.
package net.grinder.console.swingui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Image;
import java.awt.SystemColor;
import java.awt.event.ActionEvent;
import java.awt.event.ContainerAdapter;
import java.awt.event.ContainerEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JToolBar;
import javax.swing.ProgressMonitor;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileFilter;
import net.grinder.common.Closer;
import net.grinder.common.GrinderException;
import net.grinder.common.GrinderProperties;
import net.grinder.common.UncheckedInterruptedException;
import net.grinder.console.ConsoleFoundation;
import net.grinder.console.common.ConsoleException;
import net.grinder.console.common.ErrorHandler;
import net.grinder.console.common.Resources;
import net.grinder.console.communication.ProcessControl;
import net.grinder.console.distribution.AgentCacheState;
import net.grinder.console.distribution.FileChangeWatcher;
import net.grinder.console.distribution.FileDistribution;
import net.grinder.console.distribution.FileDistributionHandler;
import net.grinder.console.editor.Buffer;
import net.grinder.console.editor.EditorModel;
import net.grinder.console.model.ConsoleProperties;
import net.grinder.console.model.SampleListener;
import net.grinder.console.model.SampleModel;
import net.grinder.console.model.SampleModelViews;
import net.grinder.statistics.StatisticsSet;
import net.grinder.util.Directory;
import net.grinder.util.FileContents;
import net.grinder.util.thread.Condition;
import org.slf4j.Logger;
/**
* Swing UI for console.
*
* Thinking about hacking this? See
* http://madbean.com/blog/2004/17/totallygridbag.html first.
*
* @author Philip Aston
*/
public final class ConsoleUI implements ConsoleFoundation.UI {
// Do not initialise any Swing components before the constructor has
// set the Look and Feel to avoid loading the default Look and Feel
// unnecessarily.
private final LookAndFeel m_lookAndFeel;
private final ActionTable m_actionTable = new ActionTable();
private final CloseFileAction m_closeFileAction;
private final StartAction m_startAction;
private final ExitAction m_exitAction;
private final StopAction m_stopAction;
private final SaveFileAction m_saveFileAction;
private final SaveFileAsAction m_saveFileAsAction;
private final DistributeFilesAction m_distributeFilesAction;
private final Resources m_resources;
private final ConsoleProperties m_properties;
private final SampleModel m_model;
private final SampleModelViews m_sampleModelViews;
private final ProcessControl m_processControl;
private final FileDistribution m_fileDistribution;
private final EditorModel m_editorModel;
private final JFrame m_frame;
private final FrameBounds m_frameBounds;
private final JLabel m_stateLabel;
private final SamplingControlPanel m_samplingControlPanel;
private final ErrorHandler m_errorHandler;
private final OptionalConfirmDialog m_optionalConfirmDialog;
private final Font m_titleLabelFont;
private final CumulativeStatisticsTableModel m_cumulativeTableModel;
/**
* Creates a new ConsoleUI
instance.
*
* @param resources Resources.
* @param consoleProperties Console properties.
* @param model The console model.
* @param sampleModelViews Console sample model views.
* @param processControl ProcessReport control.
* @param fileDistribution File distribution.
* @param logger Logger to use.
* @exception ConsoleException if an error occurs
*/
public ConsoleUI(Resources resources,
ConsoleProperties consoleProperties,
SampleModel model,
SampleModelViews sampleModelViews,
ProcessControl processControl,
FileDistribution fileDistribution,
Logger logger)
throws ConsoleException {
m_resources = resources;
m_properties = consoleProperties;
m_model = model;
m_sampleModelViews = sampleModelViews;
m_processControl = processControl;
m_fileDistribution = fileDistribution;
// Create the frame to contain the a menu and the top level pane.
// Do before actions are constructed as we use the frame to create dialogs.
m_frame = new JFrame(m_resources.getString("title"));
final ErrorDialogHandler errorDialogHandler =
new ErrorDialogHandler(m_frame, m_resources, logger);
m_errorHandler =
new SwingDispatcherFactoryImplementation(null).create(ErrorHandler.class,
errorDialogHandler);
final SwingDispatcherFactory swingDispatcherFactory =
new SwingDispatcherFactoryImplementation(m_errorHandler);
// LookAndFeel constructor will set initial Look and Feel from properties.
m_lookAndFeel = new LookAndFeel(m_properties, swingDispatcherFactory);
errorDialogHandler.registerWithLookAndFeel(m_lookAndFeel);
m_editorModel = new EditorModel(m_resources,
new Editor.TextSourceFactory(),
m_fileDistribution.getAgentCacheState(),
m_fileDistribution);
m_editorModel.setExternalEditor(m_properties.getExternalEditorCommand(),
m_properties.getExternalEditorArguments());
m_editorModel.setSelectedPropertiesFile(m_properties.getPropertiesFile());
m_properties.addPropertyChangeListener(
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
if (e.getPropertyName().equals(
ConsoleProperties.EXTERNAL_EDITOR_COMMAND_PROPERTY) ||
e.getPropertyName().equals(
ConsoleProperties.EXTERNAL_EDITOR_ARGUMENTS_PROPERTY)) {
m_editorModel.setExternalEditor(
m_properties.getExternalEditorCommand(),
m_properties.getExternalEditorArguments());
}
else if (e.getPropertyName().equals(
ConsoleProperties.PROPERTIES_FILE_PROPERTY)) {
m_editorModel.setSelectedPropertiesFile(
m_properties.getPropertiesFile());
}
}
});
m_optionalConfirmDialog =
new OptionalConfirmDialog(m_frame, m_resources, m_properties);
m_closeFileAction = new CloseFileAction();
m_exitAction = new ExitAction();
m_startAction = new StartAction();
m_stopAction = new StopAction();
m_saveFileAction = new SaveFileAction();
m_saveFileAsAction = new SaveFileAsAction();
m_distributeFilesAction = new DistributeFilesAction();
m_actionTable.add(m_closeFileAction);
m_actionTable.add(m_exitAction);
m_actionTable.add(m_startAction);
m_actionTable.add(m_stopAction);
m_actionTable.add(m_saveFileAsAction);
m_actionTable.add(m_distributeFilesAction);
m_actionTable.add(new AboutAction(m_resources.getImageIcon("logo.image")));
m_actionTable.add(new ChooseDirectoryAction());
m_actionTable.add(new StartProcessesAction());
m_actionTable.add(new NewFileAction());
m_actionTable.add(new OptionsAction());
m_actionTable.add(new ResetProcessesAction());
m_actionTable.add(new SaveFileAction());
m_actionTable.add(new SaveResultsAction());
m_actionTable.add(new StopProcessesAction());
m_stateLabel = new JLabel();
stateChanged();
m_samplingControlPanel = new SamplingControlPanel(m_resources);
final JPanel controlAndTotalPanel = createControlAndTotalPanel();
// Create the tabbed test display.
final JTabbedPane tabbedPane = new JTabbedPane();
final TestGraphPanel graphPanel =
new TestGraphPanel(tabbedPane,
m_model,
m_sampleModelViews,
m_resources,
swingDispatcherFactory);
graphPanel.resetTests(); // Show logo.
final JScrollPane graphTabPane =
new JScrollPane(graphPanel,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
graphTabPane.setBorder(BorderFactory.createEmptyBorder());
tabbedPane.addTab(m_resources.getString("graphTab.title"),
m_resources.getImageIcon("graphTab.image"),
graphTabPane,
m_resources.getString("graphTab.tip"));
m_titleLabelFont =
new JLabel().getFont().deriveFont(Font.PLAIN | Font.ITALIC);
m_cumulativeTableModel =
new CumulativeStatisticsTableModel(
m_model, m_sampleModelViews, m_resources, swingDispatcherFactory);
final JScrollPane cumulativeTablePane =
new JScrollPane(new Table(m_cumulativeTableModel));
cumulativeTablePane.setBorder(createTitledBorder("cumulativeTable.label"));
cumulativeTablePane.setMinimumSize(new Dimension(100, 60));
final SampleStatisticsTableModel sampleModel =
new SampleStatisticsTableModel(
m_model, m_sampleModelViews, m_resources, swingDispatcherFactory);
final JScrollPane sampleTablePane = new JScrollPane(new Table(sampleModel));
sampleTablePane.setBorder(createTitledBorder("sampleTable.label"));
sampleTablePane.setMinimumSize(new Dimension(100, 60));
final JSplitPane resultsPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
cumulativeTablePane,
sampleTablePane);
resultsPane.setOneTouchExpandable(true);
resultsPane.setResizeWeight(1.0d);
resultsPane.setBorder(BorderFactory.createEmptyBorder());
tabbedPane.addTab(m_resources.getString("resultsTab.title"),
m_resources.getImageIcon("resultsTab.image"),
resultsPane,
m_resources.getString("resultsTab.tip"));
final ProcessStatusTableModel processStatusModel =
new ProcessStatusTableModel(m_resources,
m_processControl,
swingDispatcherFactory);
final JScrollPane processStatusPane =
new JScrollPane(new Table(processStatusModel));
processStatusPane.setBorder(
createTitledBorder("processStatusTableTab.tip"));
tabbedPane.addTab(m_resources.getString("processStatusTableTab.title"),
m_resources.getImageIcon(
"processStatusTableTab.image"),
processStatusPane,
m_resources.getString("processStatusTableTab.tip"));
final JToolBar editorToolBar = new JToolBar();
new ToolBarAssembler(editorToolBar, true).populate("editor.toolbar");
final Font editorSmallFont =
m_titleLabelFont.deriveFont(Font.PLAIN)
.deriveFont(m_titleLabelFont.getSize2D() * 0.8f);
final EditorControls editorControls =
new EditorControls(
m_resources, m_editorModel, editorSmallFont, editorToolBar);
final Editor editor = new Editor(m_editorModel, m_saveFileAction);
final FileTreeModel fileTreeModel =
new FileTreeModel(m_editorModel,
m_fileDistribution.getDistributionFileFilter(),
m_properties.getDistributionDirectory().getFile());
m_properties.addPropertyChangeListener(
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
if (e.getPropertyName().equals(
ConsoleProperties.DISTRIBUTION_DIRECTORY_PROPERTY)) {
fileTreeModel.setRootDirectory(
m_properties.getDistributionDirectory().getFile());
}
}
});
m_fileDistribution.addFileChangedListener(
swingDispatcherFactory.create(
FileChangeWatcher.FileChangedListener.class,
fileTreeModel.new RefreshChangedDirectoriesListener()));
final JPopupMenu fileTreePopupMenu = new JPopupMenu();
final FileTree fileTree = new FileTree(m_resources,
getErrorHandler(),
m_editorModel,
new BufferTreeModel(m_editorModel),
fileTreeModel,
editorSmallFont,
fileTreePopupMenu,
m_properties);
final CustomAction[] fileTreeActions = fileTree.getActions();
for (int i = 0; i < fileTreeActions.length; ++i) {
m_actionTable.add(fileTreeActions[i]);
}
new PopupMenuAssembler(fileTreePopupMenu).populate(
"editor.filetree.popupmenu");
final JPanel editorPanel = new JPanel();
editorPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
editorPanel.add(editorControls.getComponent());
editorPanel.add(editor.getComponent());
final JToolBar fileTreeToolBar = new JToolBar();
new ToolBarAssembler(fileTreeToolBar, true).populate("filetree.toolbar");
fileTreeToolBar.setFloatable(false);
fileTreeToolBar.setAlignmentX(Component.LEFT_ALIGNMENT);
final JComponent fileTreeComponent = fileTree.getComponent();
fileTreeComponent.setAlignmentX(Component.LEFT_ALIGNMENT);
fileTreeComponent.setPreferredSize(new Dimension(200, 100));
final JPanel fileTreeControlPanel = new JPanel();
fileTreeControlPanel.setLayout(
new BoxLayout(fileTreeControlPanel, BoxLayout.Y_AXIS));
fileTreeControlPanel.setBorder(BorderFactory.createEmptyBorder());
fileTreeControlPanel.add(fileTreeToolBar);
fileTreeControlPanel.add(fileTreeComponent);
final JSplitPane scriptPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
fileTreeControlPanel,
editorPanel);
scriptPane.setOneTouchExpandable(true);
scriptPane.setBorder(BorderFactory.createEmptyBorder());
tabbedPane.addTab(m_resources.getString("scriptTab.title"),
m_resources.getImageIcon("scriptTab.image"),
scriptPane,
m_resources.getString("scriptTab.tip"));
final JPanel contentPanel = new JPanel(new BorderLayout());
contentPanel.add(controlAndTotalPanel, BorderLayout.WEST);
contentPanel.add(tabbedPane, BorderLayout.CENTER);
// Create a panel to hold the tool bar and the test pane.
final JPanel toolBarPanel = new JPanel(new BorderLayout());
final JToolBar mainToolBar = new JToolBar();
new ToolBarAssembler(mainToolBar, false).populate("main.toolbar");
toolBarPanel.add(mainToolBar, BorderLayout.NORTH);
toolBarPanel.add(contentPanel, BorderLayout.CENTER);
m_frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
m_frame.addWindowListener(new WindowCloseAdapter());
final Container topLevelPane = m_frame.getContentPane();
final JMenuBar menuBar = new JMenuBar();
new MenuBarAssembler(menuBar).populate("menubar");
topLevelPane.add(menuBar, BorderLayout.NORTH);
topLevelPane.add(toolBarPanel, BorderLayout.CENTER);
final ImageIcon logoIcon = m_resources.getImageIcon("logo.image");
if (logoIcon != null) {
final Image logoImage = logoIcon.getImage();
if (logoImage != null) {
// We've a high resolution image, but the Ubuntu Unity switcher
// renders it in low resolution. The launcher looks OK though.
// I think this is a Unity bug.
m_frame.setIconImage(logoImage);
}
}
m_model.addModelListener(
swingDispatcherFactory.create(
SampleModel.Listener.class,
new SampleModel.AbstractListener() {
public void stateChanged() { ConsoleUI.this.stateChanged(); }
}
));
m_lookAndFeel.addListener(new LookAndFeelListener());
m_frameBounds = new FrameBounds(m_properties, m_frame);
m_frameBounds.restore();
resultsPane.setDividerLocation(resultsPane.getMaximumDividerLocation());
m_frame.setVisible(true);
}
private TitledBorder createTitledBorder(String titleResource) {
final TitledBorder border =
BorderFactory.createTitledBorder(
BorderFactory.createEmptyBorder(3, 3, 3, 3),
m_resources.getString(titleResource));
border.setTitleFont(m_titleLabelFont);
border.setTitleColor(SystemColor.textInactiveText);
border.setTitleJustification(TitledBorder.RIGHT);
return border;
}
private JPanel createControlAndTotalPanel() {
final LabelledGraph totalGraph =
new LabelledGraph(m_resources.getString("totalGraph.title"),
m_resources, SystemColor.window,
m_model.getTPSExpression(),
m_model.getPeakTPSExpression(),
m_sampleModelViews.getTestStatisticsQueries());
final JLabel tpsLabel = new JLabel();
tpsLabel.setFont(new Font("helvetica", Font.ITALIC | Font.BOLD, 40));
tpsLabel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10));
m_model.addTotalSampleListener(
new SampleListener() {
private final String m_suffix =
' ' + m_resources.getString("tps.units");
public void update(StatisticsSet intervalStatistics,
StatisticsSet cumulativeStatistics) {
final NumberFormat format = m_sampleModelViews.getNumberFormat();
tpsLabel.setText(
format.format(
m_model.getTPSExpression().getDoubleValue(intervalStatistics)) +
m_suffix);
totalGraph.add(intervalStatistics, cumulativeStatistics, format);
}
});
final JButton stateButton = new CustomJButton();
stateButton.setBorderPainted(true);
stateButton.setAction(m_stopAction);
stateButton.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
m_stopAction.registerButton(stateButton);
m_stateLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 0));
final JPanel statePanel = new JPanel();
statePanel.setLayout(new BoxLayout(statePanel, BoxLayout.X_AXIS));
statePanel.add(stateButton);
statePanel.add(m_stateLabel);
statePanel.setAlignmentX(Component.LEFT_ALIGNMENT);
m_samplingControlPanel.add(Box.createRigidArea(new Dimension(0, 40)));
m_samplingControlPanel.add(statePanel);
m_samplingControlPanel.setBorder(
BorderFactory.createEmptyBorder(10, 10, 0, 10));
m_samplingControlPanel.setProperties(m_properties);
final JPanel controlAndTotalPanel = new JPanel();
controlAndTotalPanel.setLayout(
new BoxLayout(controlAndTotalPanel, BoxLayout.Y_AXIS));
m_samplingControlPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
tpsLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
totalGraph.setAlignmentX(Component.LEFT_ALIGNMENT);
controlAndTotalPanel.add(m_samplingControlPanel);
controlAndTotalPanel.add(Box.createRigidArea(new Dimension(0, 100)));
controlAndTotalPanel.add(tpsLabel);
controlAndTotalPanel.add(Box.createRigidArea(new Dimension(0, 20)));
controlAndTotalPanel.add(totalGraph);
final JPanel hackToFixLayout = new JPanel();
hackToFixLayout.add(controlAndTotalPanel);
return hackToFixLayout;
}
private final class LookAndFeelListener
extends LookAndFeel.ComponentListener {
private LookAndFeelListener() {
super(m_frame);
}
public void lookAndFeelChanged() {
m_frame.setVisible(false);
try {
m_frameBounds.store();
}
catch (ConsoleException e) {
getErrorHandler().handleException(e);
}
super.lookAndFeelChanged();
m_frameBounds.restore();
m_frame.setVisible(true);
}
}
private abstract class ListTokeniserTemplate {
private final JComponent m_component;
protected ListTokeniserTemplate(JComponent component) {
m_component = component;
}
public void populate(String key) {
final String tokens = m_resources.getString(key);
final List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy