// $Header: /home/cvs/jakarta-jmeter/src/core/org/apache/jmeter/gui/MainFrame.java,v 1.24 2004/02/13 02:40:53 sebb Exp $
/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* Licensed 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.jmeter.gui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.HashSet;
import java.util.Set;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTree;
import javax.swing.MenuElement;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeModel;
import org.apache.jmeter.engine.event.LoopIterationEvent;
import org.apache.jmeter.gui.action.ActionRouter;
import org.apache.jmeter.gui.action.GlobalMouseListener;
import org.apache.jmeter.gui.tree.JMeterCellRenderer;
import org.apache.jmeter.gui.tree.JMeterTreeListener;
import org.apache.jmeter.gui.util.JMeterMenuBar;
import org.apache.jmeter.samplers.Remoteable;
import org.apache.jmeter.testelement.TestListener;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.gui.ComponentUtil;
/**
* The main JMeter frame, containing the menu bar, test tree, and an area for
* JMeter component GUIs.
*
* @author Michael Stover
* @version $Revision: 1.24 $
*/
public class MainFrame extends JFrame implements TestListener, Remoteable
{
/** The menu bar. */
private JMeterMenuBar menuBar;
/** The main panel where components display their GUIs. */
private JScrollPane mainPanel;
/** The panel where the test tree is shown. */
private JScrollPane treePanel;
/** The test tree. */
private JTree tree;
/** An image which is displayed when a test is running. */
private ImageIcon runningIcon = JMeterUtils.getImage("thread.enabled.gif");
/** An image which is displayed when a test is not currently running. */
private ImageIcon stoppedIcon = JMeterUtils.getImage("thread.disabled.gif");
/** The button used to display the running/stopped image. */
private JButton runningIndicator;
/** The x coordinate of the last location where a component was dragged. */
private int previousDragXLocation = 0;
/** The y coordinate of the last location where a component was dragged. */
private int previousDragYLocation = 0;
/** The set of currently running hosts. */
private Set hosts = new HashSet();
/** A message dialog shown while JMeter threads are stopping. */
private JDialog stoppingMessage;
/**
* Create a new JMeter frame.
*
* @param actionHandler this parameter is not used
* @param treeModel the model for the test tree
* @param treeListener the listener for the test tree
*/
public MainFrame(
ActionListener actionHandler,
TreeModel treeModel,
JMeterTreeListener treeListener)
{
// TODO: actionHandler isn't used -- remove it from the parameter list
//this.actionHandler = actionHandler;
// TODO: Make the running indicator its own class instead of a JButton
runningIndicator = new JButton(stoppedIcon);
runningIndicator.setMargin(new Insets(0, 0, 0, 0));
runningIndicator.setBorder(BorderFactory.createEmptyBorder());
tree = makeTree(treeModel, treeListener);
GuiPackage.getInstance().setMainFrame(this);
init();
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
}
/**
* Default constructor for the JMeter frame. This constructor will not
* properly initialize the tree, so don't use it.
*/
public MainFrame()
{
// TODO: Can we remove this constructor? JMeter won't behave properly
// if it used.
}
// MenuBar related methods
// TODO: Do we really need to have all these menubar methods duplicated
// here? Perhaps we can make the menu bar accessible through GuiPackage?
/**
* Specify whether or not the File|Load menu item should be enabled.
*
* @param enabled true if the menu item should be enabled, false otherwise
*/
public void setFileLoadEnabled(boolean enabled)
{
menuBar.setFileLoadEnabled(enabled);
}
/**
* Specify whether or not the File|Save menu item should be enabled.
*
* @param enabled true if the menu item should be enabled, false otherwise
*/
public void setFileSaveEnabled(boolean enabled)
{
menuBar.setFileSaveEnabled(enabled);
}
/**
* Set the menu that should be used for the Edit menu.
*
* @param menu the new Edit menu
*/
public void setEditMenu(JPopupMenu menu)
{
menuBar.setEditMenu(menu);
}
/**
* Specify whether or not the Edit menu item should be enabled.
*
* @param enabled true if the menu item should be enabled, false otherwise
*/
public void setEditEnabled(boolean enabled)
{
menuBar.setEditEnabled(enabled);
}
/**
* Set the menu that should be used for the Edit|Add menu.
*
* @param menu the new Edit|Add menu
*/
public void setEditAddMenu(JMenu menu)
{
menuBar.setEditAddMenu(menu);
}
/**
* Specify whether or not the Edit|Add menu item should be enabled.
*
* @param enabled true if the menu item should be enabled, false otherwise
*/
public void setEditAddEnabled(boolean enabled)
{
menuBar.setEditAddEnabled(enabled);
}
/**
* Specify whether or not the Edit|Remove menu item should be enabled.
*
* @param enabled true if the menu item should be enabled, false otherwise
*/
public void setEditRemoveEnabled(boolean enabled)
{
menuBar.setEditRemoveEnabled(enabled);
}
/**
* Close the currently selected menu.
*/
public void closeMenu()
{
if (menuBar.isSelected())
{
MenuElement[] menuElement = menuBar.getSubElements();
if (menuElement != null)
{
for (int i = 0; i < menuElement.length; i++)
{
JMenu menu = (JMenu) menuElement[i];
if (menu.isSelected())
{
menu.setPopupMenuVisible(false);
menu.setSelected(false);
break;
}
}
}
}
}
/**
* Show a dialog indicating that JMeter threads are stopping on a
* particular host.
*
* @param host the host where JMeter threads are stopping
*/
public void showStoppingMessage(String host)
{
stoppingMessage =
new JDialog(
this,
JMeterUtils.getResString("stopping_test_title"),
true);
JLabel stopLabel =
new JLabel(JMeterUtils.getResString("stopping_test") + ": " + host);
stopLabel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
stoppingMessage.getContentPane().add(stopLabel);
stoppingMessage.pack();
ComponentUtil.centerComponentInComponent(this, stoppingMessage);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
if (stoppingMessage != null)
{
stoppingMessage.show();
}
}
});
}
/****************************************
* !ToDo (Method description)
*
*@param comp !ToDo (Parameter description)
***************************************/
public void setMainPanel(JComponent comp)
{
mainPanel.setViewportView(comp);
}
/****************************************
* !ToDoo (Method description)
*
*@return !ToDo (Return description)
***************************************/
public JTree getTree()
{
return tree;
}
// TestListener implementation
/**
* Called when a test is started on the local system. This implementation
* sets the running indicator and ensures that the menubar is enabled and
* in the running state.
*/
public void testStarted()
{
testStarted("local");
menuBar.setEnabled(true);
}
/**
* Called when a test is started on a specific host. This implementation
* sets the running indicator and ensures that the menubar is in the
* running state.
*
* @param host the host where the test is starting
*/
public void testStarted(String host)
{
hosts.add(host);
runningIndicator.setIcon(runningIcon);
menuBar.setRunning(true, host);
}
/**
* Called when a test is ended on the local system. This implementation
* disables the menubar, stops the running indicator, and closes the
* stopping message dialog.
*/
public void testEnded()
{
testEnded("local");
menuBar.setEnabled(false);
}
/**
* Called when a test is ended on the remote system. This implementation
* stops the running indicator and closes the stopping message dialog.
*
* @param host the host where the test is ending
*/
public void testEnded(String host)
{
hosts.remove(host);
if (hosts.size() == 0)
{
runningIndicator.setIcon(stoppedIcon);
}
menuBar.setRunning(false, host);
if (stoppingMessage != null)
{
stoppingMessage.dispose();
stoppingMessage = null;
}
}
/* Implements TestListener#testIterationStart(LoopIterationEvent) */
public void testIterationStart(LoopIterationEvent event)
{
}
/**
* Create the GUI components and layout.
*/
private void init()
{
menuBar = new JMeterMenuBar();
setJMenuBar(menuBar);
JPanel all = new JPanel(new BorderLayout());
all.add(createToolBar(), BorderLayout.NORTH);
JSplitPane treeAndMain = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
treePanel = createTreePanel();
treeAndMain.setLeftComponent(treePanel);
mainPanel = createMainPanel();
treeAndMain.setRightComponent(mainPanel);
treeAndMain.setResizeWeight(.2);
treeAndMain.setContinuousLayout(true);
all.add(treeAndMain, BorderLayout.CENTER);
getContentPane().add(all);
tree.setSelectionRow(1);
addWindowListener(new WindowHappenings());
addMouseListener(new GlobalMouseListener());
}
/**
* Create the JMeter tool bar pane containing the running indicator.
*
* @return a panel containing the running indicator
*/
private Component createToolBar()
{
Box toolPanel = new Box(BoxLayout.X_AXIS);
toolPanel.add(Box.createRigidArea(new Dimension(10, 15)));
toolPanel.add(Box.createGlue());
toolPanel.add(runningIndicator);
return toolPanel;
}
/**
* Create the panel where the GUI representation of the test tree is
* displayed. The tree should already be created before calling this
* method.
*
* @return a scroll pane containing the test tree GUI
*/
private JScrollPane createTreePanel()
{
JScrollPane treePanel = new JScrollPane(tree);
treePanel.setMinimumSize(new Dimension(100, 0));
return treePanel;
}
/**
* Create the main panel where components can display their GUIs.
*
* @return the main scroll pane
*/
private JScrollPane createMainPanel()
{
return new JScrollPane();
}
/**
* Create and initialize the GUI representation of the test tree.
*
* @param treeModel the test tree model
* @param treeListener the test tree listener
*
* @return the initialized test tree GUI
*/
private JTree makeTree(TreeModel treeModel, JMeterTreeListener treeListener)
{
JTree tree = new JTree(treeModel);
tree.setCellRenderer(getCellRenderer());
tree.setRootVisible(false);
tree.setShowsRootHandles(true);
treeListener.setJTree(tree);
tree.addTreeSelectionListener(treeListener);
tree.addMouseListener(treeListener);
tree.addMouseMotionListener(treeListener);
tree.addKeyListener(treeListener);
return tree;
}
/**
* Create the tree cell renderer used to draw the nodes in the test tree.
*
* @return a renderer to draw the test tree nodes
*/
private TreeCellRenderer getCellRenderer()
{
DefaultTreeCellRenderer rend = new JMeterCellRenderer();
rend.setFont(new Font("Dialog", Font.PLAIN, 11));
return rend;
}
/**
* Repaint pieces of the GUI as needed while dragging. This method should
* only be called from the Swing event thread.
*
* @param dragIcon the component being dragged
* @param x the current mouse x coordinate
* @param y the current mouse y coordinate
*/
public void drawDraggedComponent(Component dragIcon, int x, int y)
{
Dimension size = dragIcon.getPreferredSize();
treePanel.paintImmediately(
previousDragXLocation,
previousDragYLocation,
size.width,
size.height);
this.getLayeredPane().setLayer(dragIcon, 400);
SwingUtilities.paintComponent(
treePanel.getGraphics(),
dragIcon,
treePanel,
x,
y,
size.width,
size.height);
previousDragXLocation = x;
previousDragYLocation = y;
}
/**
* A window adapter used to detect when the main JMeter frame is being
* closed.
*/
private class WindowHappenings extends WindowAdapter
{
/**
* Called when the main JMeter frame is being closed. Sends a
* notification so that JMeter can react appropriately.
*
* @param event the WindowEvent to handle
*/
public void windowClosing(WindowEvent event)
{
ActionRouter.getInstance().actionPerformed(
new ActionEvent(this, event.getID(), "exit"));
}
}
}