001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.jmeter.gui;
020:
021: import java.awt.BorderLayout;
022: import java.awt.Component;
023: import java.awt.Dimension;
024: import java.awt.Font;
025: import java.awt.Insets;
026: import java.awt.event.ActionEvent;
027: import java.awt.event.ActionListener;
028: import java.awt.event.WindowAdapter;
029: import java.awt.event.WindowEvent;
030:
031: import javax.swing.BorderFactory;
032: import javax.swing.Box;
033: import javax.swing.BoxLayout;
034: import javax.swing.ImageIcon;
035: import javax.swing.JButton;
036: import javax.swing.JComponent;
037: import javax.swing.JDialog;
038: import javax.swing.JFrame;
039: import javax.swing.JLabel;
040: import javax.swing.JMenu;
041: import javax.swing.JPanel;
042: import javax.swing.JPopupMenu;
043: import javax.swing.JScrollPane;
044: import javax.swing.JSplitPane;
045: import javax.swing.JTree;
046: import javax.swing.MenuElement;
047: import javax.swing.SwingUtilities;
048: import javax.swing.tree.DefaultTreeCellRenderer;
049: import javax.swing.tree.TreeCellRenderer;
050: import javax.swing.tree.TreeModel;
051:
052: import org.apache.jmeter.engine.event.LoopIterationEvent;
053: import org.apache.jmeter.gui.util.ReportMenuBar;
054: import org.apache.jmeter.report.gui.action.ReportActionRouter;
055: import org.apache.jmeter.report.gui.tree.ReportCellRenderer;
056: import org.apache.jmeter.report.gui.tree.ReportTreeListener;
057: import org.apache.jmeter.samplers.Remoteable;
058: import org.apache.jmeter.testelement.TestListener;
059: import org.apache.jmeter.util.JMeterUtils;
060: import org.apache.jorphan.gui.ComponentUtil;
061: import org.apache.jorphan.logging.LoggingManager;
062: import org.apache.log.Logger;
063:
064: /**
065: * ReportMainFrame is based on MainFrame. it uses the same basic structure,
066: * but with changes for the report gui.
067: *
068: */
069: public class ReportMainFrame extends JFrame implements TestListener,
070: Remoteable {
071:
072: private static final Logger log = LoggingManager
073: .getLoggerForClass();
074:
075: // The default title for the Menu bar
076: private static final String DEFAULT_TITLE = "Apache JMeter ("
077: + JMeterUtils.getJMeterVersion() + ")"; // $NON-NLS-1$ $NON-NLS-2$
078:
079: /** The menu bar. */
080: protected ReportMenuBar menuBar;
081:
082: /** The main panel where components display their GUIs. */
083: protected JScrollPane mainPanel;
084:
085: /** The panel where the test tree is shown. */
086: protected JScrollPane treePanel;
087:
088: /** The test tree. */
089: protected JTree tree;
090:
091: /** An image which is displayed when a test is running. */
092: //private ImageIcon runningIcon = JMeterUtils.getImage("thread.enabled.gif");
093: /** An image which is displayed when a test is not currently running. */
094: private ImageIcon stoppedIcon = JMeterUtils
095: .getImage("thread.disabled.gif");// $NON-NLS-1$
096:
097: /** The x coordinate of the last location where a component was dragged. */
098: private int previousDragXLocation = 0;
099:
100: /** The y coordinate of the last location where a component was dragged. */
101: private int previousDragYLocation = 0;
102:
103: /** The button used to display the running/stopped image. */
104: private JButton runningIndicator;
105:
106: /** The set of currently running hosts. */
107: //private Set hosts = new HashSet();
108: /** A message dialog shown while JMeter threads are stopping. */
109: private JDialog stoppingMessage;
110:
111: public ReportMainFrame() {
112: log.warn("Constructor only intended for use in testing"); // $NON-NLS-1$
113: }
114:
115: /**
116: * Create a new JMeter frame.
117: *
118: * @param actionHandler
119: * this parameter is not used
120: * @param treeModel
121: * the model for the test tree
122: * @param treeListener
123: * the listener for the test tree
124: */
125: public ReportMainFrame(ActionListener actionHandler,
126: TreeModel treeModel, ReportTreeListener treeListener) {
127: runningIndicator = new JButton(stoppedIcon);
128: runningIndicator.setMargin(new Insets(0, 0, 0, 0));
129: runningIndicator.setBorder(BorderFactory.createEmptyBorder());
130:
131: this .tree = this .makeTree(treeModel, treeListener);
132:
133: ReportGuiPackage.getInstance().setMainFrame(this );
134: init();
135:
136: setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
137: }
138:
139: // MenuBar related methods
140: // TODO: Do we really need to have all these menubar methods duplicated
141: // here? Perhaps we can make the menu bar accessible through GuiPackage?
142:
143: /**
144: * Specify whether or not the File|Load menu item should be enabled.
145: *
146: * @param enabled
147: * true if the menu item should be enabled, false otherwise
148: */
149: public void setFileLoadEnabled(boolean enabled) {
150: menuBar.setFileLoadEnabled(enabled);
151: }
152:
153: /**
154: * Specify whether or not the File|Save menu item should be enabled.
155: *
156: * @param enabled
157: * true if the menu item should be enabled, false otherwise
158: */
159: public void setFileSaveEnabled(boolean enabled) {
160: menuBar.setFileSaveEnabled(enabled);
161: }
162:
163: /**
164: * Set the menu that should be used for the Edit menu.
165: *
166: * @param menu
167: * the new Edit menu
168: */
169: public void setEditMenu(JPopupMenu menu) {
170: menuBar.setEditMenu(menu);
171: }
172:
173: /**
174: * Specify whether or not the Edit menu item should be enabled.
175: *
176: * @param enabled
177: * true if the menu item should be enabled, false otherwise
178: */
179: public void setEditEnabled(boolean enabled) {
180: menuBar.setEditEnabled(enabled);
181: }
182:
183: /**
184: * Set the menu that should be used for the Edit|Add menu.
185: *
186: * @param menu
187: * the new Edit|Add menu
188: */
189: public void setEditAddMenu(JMenu menu) {
190: menuBar.setEditAddMenu(menu);
191: }
192:
193: /**
194: * Specify whether or not the Edit|Add menu item should be enabled.
195: *
196: * @param enabled
197: * true if the menu item should be enabled, false otherwise
198: */
199: public void setEditAddEnabled(boolean enabled) {
200: menuBar.setEditAddEnabled(enabled);
201: }
202:
203: /**
204: * Specify whether or not the Edit|Remove menu item should be enabled.
205: *
206: * @param enabled
207: * true if the menu item should be enabled, false otherwise
208: */
209: public void setEditRemoveEnabled(boolean enabled) {
210: menuBar.setEditRemoveEnabled(enabled);
211: }
212:
213: /**
214: * Close the currently selected menu.
215: */
216: public void closeMenu() {
217: if (menuBar.isSelected()) {
218: MenuElement[] menuElement = menuBar.getSubElements();
219: if (menuElement != null) {
220: for (int i = 0; i < menuElement.length; i++) {
221: JMenu menu = (JMenu) menuElement[i];
222: if (menu.isSelected()) {
223: menu.setPopupMenuVisible(false);
224: menu.setSelected(false);
225: break;
226: }
227: }
228: }
229: }
230: }
231:
232: /**
233: * Show a dialog indicating that JMeter threads are stopping on a particular
234: * host.
235: *
236: * @param host
237: * the host where JMeter threads are stopping
238: */
239: public void showStoppingMessage(String host) {
240: stoppingMessage = new JDialog(this , JMeterUtils
241: .getResString("stopping_test_title"), true);// $NON-NLS-1$
242: JLabel stopLabel = new JLabel(JMeterUtils
243: .getResString("stopping_test")
244: + ": " + host);// $NON-NLS-1$
245: stopLabel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20,
246: 20));
247: stoppingMessage.getContentPane().add(stopLabel);
248: stoppingMessage.pack();
249: ComponentUtil.centerComponentInComponent(this , stoppingMessage);
250: SwingUtilities.invokeLater(new Runnable() {
251: public void run() {
252: if (stoppingMessage != null) {
253: stoppingMessage.show();
254: }
255: }
256: });
257: }
258:
259: public void setMainPanel(JComponent comp) {
260: mainPanel.setViewportView(comp);
261: }
262:
263: public JTree getTree() {
264: return this .tree;
265: }
266:
267: // TestListener implementation
268:
269: /**
270: * Not sure if this should be in the ReportMainFrame, since the
271: * report component doesn't really test, it generates reports. for
272: * now, I will use it to trigger reporting. Later we can refactor
273: * MainFrame and create an abstract base class.
274: */
275: public void testStarted() {
276:
277: // super.testStarted();
278: }
279:
280: /**
281: * Not sure if this should be in the ReportMainFrame, since the
282: * report component doesn't really test, it generates reports. for
283: * now, I will use it to trigger reporting. Later we can refactor
284: * MainFrame and create an abstract base class.
285: */
286: public void testStarted(String host) {
287: // super.testStarted(host);
288: }
289:
290: /**
291: * Not sure if this should be in the ReportMainFrame, since the
292: * report component doesn't really test, it generates reports. for
293: * now, I will use it to trigger reporting. Later we can refactor
294: * MainFrame and create an abstract base class.
295: */
296: public void testEnded() {
297: // super.testEnded();
298: }
299:
300: /**
301: * Not sure if this should be in the ReportMainFrame, since the
302: * report component doesn't really test, it generates reports. for
303: * now, I will use it to trigger reporting. Later we can refactor
304: * MainFrame and create an abstract base class.
305: */
306: public void testEnded(String host) {
307: // super.testEnded(host);
308: }
309:
310: /* Implements TestListener#testIterationStart(LoopIterationEvent) */
311: public void testIterationStart(LoopIterationEvent event) {
312: }
313:
314: /**
315: * Create the GUI components and layout.
316: */
317: protected void init() {
318: menuBar = new ReportMenuBar();
319: setJMenuBar(menuBar);
320:
321: JPanel all = new JPanel(new BorderLayout());
322: all.add(createToolBar(), BorderLayout.NORTH);
323:
324: JSplitPane treeAndMain = new JSplitPane(
325: JSplitPane.HORIZONTAL_SPLIT);
326:
327: treePanel = createTreePanel();
328: treeAndMain.setLeftComponent(treePanel);
329:
330: mainPanel = createMainPanel();
331: treeAndMain.setRightComponent(mainPanel);
332:
333: treeAndMain.setResizeWeight(.2);
334: treeAndMain.setContinuousLayout(true);
335: all.add(treeAndMain, BorderLayout.CENTER);
336:
337: getContentPane().add(all);
338:
339: tree.setSelectionRow(1);
340: addWindowListener(new WindowHappenings());
341:
342: setTitle(DEFAULT_TITLE);
343: setIconImage(JMeterUtils.getImage("jmeter.jpg").getImage());// $NON-NLS-1$
344: }
345:
346: public void setExtendedFrameTitle(String fname) {
347: // file New operation may set to null, so just return app name
348: if (fname == null) {
349: setTitle(DEFAULT_TITLE);
350: return;
351: }
352:
353: // allow for windows / chars in filename
354: String temp = fname.replace('\\', '/'); // $NON-NLS-1$ // $NON-NLS-2$
355: String simpleName = temp.substring(temp.lastIndexOf("/") + 1);// $NON-NLS-1$
356: setTitle(simpleName + " (" + fname + ") - " + DEFAULT_TITLE); // $NON-NLS-1$ // $NON-NLS-2$
357: }
358:
359: /**
360: * Create the JMeter tool bar pane containing the running indicator.
361: *
362: * @return a panel containing the running indicator
363: */
364: protected Component createToolBar() {
365: Box toolPanel = new Box(BoxLayout.X_AXIS);
366: toolPanel.add(Box.createRigidArea(new Dimension(10, 15)));
367: toolPanel.add(Box.createGlue());
368: toolPanel.add(runningIndicator);
369: return toolPanel;
370: }
371:
372: /**
373: * Create the panel where the GUI representation of the test tree is
374: * displayed. The tree should already be created before calling this method.
375: *
376: * @return a scroll pane containing the test tree GUI
377: */
378: protected JScrollPane createTreePanel() {
379: JScrollPane treeP = new JScrollPane(tree);
380: treeP.setMinimumSize(new Dimension(100, 0));
381: return treeP;
382: }
383:
384: /**
385: * Create the main panel where components can display their GUIs.
386: *
387: * @return the main scroll pane
388: */
389: protected JScrollPane createMainPanel() {
390: return new JScrollPane();
391: }
392:
393: /**
394: * Create and initialize the GUI representation of the test tree.
395: *
396: * @param treeModel
397: * the test tree model
398: * @param treeListener
399: * the test tree listener
400: *
401: * @return the initialized test tree GUI
402: */
403: private JTree makeTree(TreeModel treeModel,
404: ReportTreeListener treeListener) {
405: JTree treevar = new JTree(treeModel);
406: treevar.setCellRenderer(getCellRenderer());
407: treevar.setRootVisible(false);
408: treevar.setShowsRootHandles(true);
409:
410: treeListener.setJTree(treevar);
411: treevar.addTreeSelectionListener(treeListener);
412: treevar.addMouseListener(treeListener);
413: treevar.addMouseMotionListener(treeListener);
414: treevar.addKeyListener(treeListener);
415:
416: return treevar;
417: }
418:
419: /**
420: * Create the tree cell renderer used to draw the nodes in the test tree.
421: *
422: * @return a renderer to draw the test tree nodes
423: */
424: protected TreeCellRenderer getCellRenderer() {
425: DefaultTreeCellRenderer rend = new ReportCellRenderer();
426: rend.setFont(new Font("Dialog", Font.PLAIN, 11));
427: return rend;
428: }
429:
430: public void drawDraggedComponent(Component dragIcon, int x, int y) {
431: Dimension size = dragIcon.getPreferredSize();
432: treePanel.paintImmediately(previousDragXLocation,
433: previousDragYLocation, size.width, size.height);
434: this .getLayeredPane().setLayer(dragIcon, 400);
435: SwingUtilities.paintComponent(treePanel.getGraphics(),
436: dragIcon, treePanel, x, y, size.width, size.height);
437: previousDragXLocation = x;
438: previousDragYLocation = y;
439: }
440:
441: /**
442: * A window adapter used to detect when the main JMeter frame is being
443: * closed.
444: */
445: protected static class WindowHappenings extends WindowAdapter {
446: /**
447: * Called when the main JMeter frame is being closed. Sends a
448: * notification so that JMeter can react appropriately.
449: *
450: * @param event
451: * the WindowEvent to handle
452: */
453: public void windowClosing(WindowEvent event) {
454: ReportActionRouter.getInstance().actionPerformed(
455: new ActionEvent(this , event.getID(), "exit"));
456: }
457: }
458: }
|