cdc.perfs.ui.swing.MainFrame Maven / Gradle / Ivy
package cdc.perfs.ui.swing;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JTabbedPane;
import javax.swing.SwingWorker;
import javax.swing.ToolTipManager;
import javax.swing.WindowConstants;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import javax.swing.plaf.TabbedPaneUI;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import cdc.io.utils.ProgressControllerInputStream;
import cdc.perfs.core.Context;
import cdc.perfs.core.Environment;
import cdc.perfs.core.io.IoExtension;
import cdc.perfs.core.io.IoMode;
import cdc.perfs.core.io.PerfsIo;
import cdc.perfs.core.runtime.RuntimeEnvironment;
import cdc.perfs.core.snapshot.SnapshotEnvironment;
import cdc.perfs.ui.PerfsChartHelper;
import cdc.perfs.ui.RefreshRate;
import cdc.perfs.ui.Rendering;
import cdc.ui.swing.FileNameSuffixFilter;
import cdc.ui.swing.SwingUtils;
import cdc.ui.swing.progress.SwingProgressController;
import cdc.ui.swing.widgets.TabCloseButton;
import cdc.util.files.Files;
/**
* Frame wrapper around EnvironmentPanels.
*
* It contains a Runtime Environment panel and optional Snapshot environment
* panels, which are organized in a TabbedPane.
* It also has a MenuBar.
*
* @author Damien Carbonne
*
*/
public class MainFrame extends JFrame {
private static final long serialVersionUID = 1L;
protected static final Logger LOGGER = LogManager.getLogger(MainFrame.class);
static final String SNAPSHOT = "Snaphot";
protected final JTabbedPane wTabbedPane = new JTabbedPane() {
private static final long serialVersionUID = 1L;
@Override
public String getToolTipText(MouseEvent e) {
// We do this so that click events are still interpreted to change tab
final int index = ((TabbedPaneUI) ui).tabForCoordinate(this, e.getX(), e.getY());
if (index != -1) {
final JComponent component = (JComponent) getTabComponentAt(index);
if (component != null) {
return component.getToolTipText();
}
}
return super.getToolTipText(e);
}
};
private final EnvironmentPanel wRuntimePanel;
private final List wEnvironmentPanels = new ArrayList<>();
protected About wAbout = null;
private final JMenu wRefreshRate = new JMenu("Refresh rate");
private final JMenu wRendering = new JMenu("Rendering");
private final JCheckBoxMenuItem wDrawBorders = new JCheckBoxMenuItem("Draw borders");
private final JCheckBoxMenuItem wShowStats = new JCheckBoxMenuItem("Show stats");
/** Directory to use for Open File dialog. */
protected File openDir = Files.currentDir();
/**
* Creates the frame.
*
* @param withRuntimePanel If {@code true}, the runtime panel is created.
* @param operation The value to pass to setDefaultCloseOperation().
*/
public MainFrame(boolean withRuntimePanel,
int operation) {
super();
setIconImage(SwingUtils.getApplicationImage());
if (withRuntimePanel) {
setTitle("CDC Perfs Monitor");
} else {
setTitle("CDC Perfs Viewer");
}
setDefaultCloseOperation(operation);
setSize(800, 500);
if (withRuntimePanel) {
wRuntimePanel = new EnvironmentPanel(this, RuntimeEnvironment.getInstance());
wEnvironmentPanels.add(wRuntimePanel);
wTabbedPane.addTab("Runtime", wRuntimePanel);
} else {
wRuntimePanel = null;
}
setContentPane(wTabbedPane);
buildMenus();
ToolTipManager.sharedInstance().registerComponent(wTabbedPane);
}
/**
* Creates the frame that will exit on close.
*
* @param withRuntimePanel If {@code true}, the runtime panel is created.
*/
public MainFrame(boolean withRuntimePanel) {
this(withRuntimePanel, WindowConstants.EXIT_ON_CLOSE);
}
/**
* Creates the frame that will exit on close and contains runtime panel.
*/
public MainFrame() {
this(true);
}
private Rendering getRendering() {
for (int index = 0; index < wRendering.getItemCount(); index++) {
final JRadioButtonMenuItem item = (JRadioButtonMenuItem) wRendering.getItem(index);
if (item.isSelected()) {
return Rendering.valueOf(item.getActionCommand());
}
}
// Should not happen
return Rendering.FASTEST;
}
public void setRendering(Rendering rendering) {
for (int index = 0; index < wRendering.getItemCount(); index++) {
final JRadioButtonMenuItem item = (JRadioButtonMenuItem) wRendering.getItem(index);
item.setSelected(Rendering.valueOf(item.getActionCommand()) == rendering);
}
}
public void setShowStats(boolean enabled) {
wShowStats.setSelected(enabled);
}
public void setDrawBorders(boolean enabled) {
wDrawBorders.setSelected(enabled);
}
/**
* @return the runtime panel (possibly {@code null}.
*/
public final EnvironmentPanel getRuntimePanel() {
return wRuntimePanel;
}
public final boolean hasRunTimePanel() {
return getRuntimePanel() != null;
}
public final List getEnvironmentPanels() {
return wEnvironmentPanels;
}
private static String getTooltip(Environment environment,
String systemId) {
return "" + (systemId == null ? "*" : systemId) + "
" + environment.getReferenceInstant() + "";
}
/**
* Create a new EnvironmentPanel and put it in a new Tab.
*
* @param environment the snapshot environment for which the panel must be
* created.
* @param systemId The system id from which the environment was issued.
*/
public final void addSnaphot(SnapshotEnvironment environment,
String systemId) {
final EnvironmentPanel wSnapshotPanel = new EnvironmentPanel(this, environment);
final String basename = systemId == null ? null : new File(systemId).getName();
// Index at which the new tab is inserted
final int index = wTabbedPane.getTabCount();
wTabbedPane.addTab(null, wSnapshotPanel);
final TabCloseButton wCloseButton = new TabCloseButton(wTabbedPane, systemId == null ? "* " + SNAPSHOT : basename);
wCloseButton.setToolTipText(getTooltip(environment, systemId));
ToolTipManager.sharedInstance().unregisterComponent(wCloseButton);
wTabbedPane.setTabComponentAt(index, wCloseButton);
final ControlledChartPanel ssDisplay = wSnapshotPanel.getControlledChartPanel();
if (hasRunTimePanel()) {
// Configure the newly created environment panel to match
// runtime panel configuration
final ControlledChartPanel rtDisplay = wRuntimePanel.getControlledChartPanel();
ssDisplay.setDisplayStatsEnabled(rtDisplay.getDisplayStatsEnabled());
ssDisplay.setRefreshRate(rtDisplay.getRefreshRate());
ssDisplay.setRendering(rtDisplay.getRendering());
ssDisplay.setDrawBordersEnabled(rtDisplay.getDrawBordersEnabled());
ssDisplay.setScale(rtDisplay.getScale());
ssDisplay.setFocusNanos(rtDisplay.getFocusNanos());
} else {
ssDisplay.setDisplayStatsEnabled(wShowStats.isSelected());
ssDisplay.setRendering(getRendering());
ssDisplay.setDrawBordersEnabled(wDrawBorders.isSelected());
// Arbitrarily remove 200 pixels for left panel
final double width = Math.max(100.0, getWidth() - 200.0);
final double scale = PerfsChartHelper.toScale(environment.getElapsedNanos(), width);
ssDisplay.setScale(scale);
ssDisplay.setFocusNanos(environment.getElapsedNanos() / 2.0);
}
// Reproduce contexts visibility
if (hasRunTimePanel()) {
final ContextsTableModel rtModel = wRuntimePanel.getContextsTableModel();
final ContextsTableModel ssModel = wSnapshotPanel.getContextsTableModel();
for (final Context rtContext : rtModel.getContexts()) {
final Context ssContext = ssModel.getContext(rtContext.getId());
if (ssContext != null) {
final boolean visible = rtModel.isVisible(rtContext);
ssModel.setVisible(ssContext, visible);
}
}
}
wEnvironmentPanels.add(wSnapshotPanel);
}
final void removeSnapshot(EnvironmentPanel panel) {
final boolean removed = wEnvironmentPanels.remove(panel);
assert removed;
}
final void processFilenameChange(EnvironmentPanel panel) {
final int index = wEnvironmentPanels.indexOf(panel);
final TabCloseButton button = (TabCloseButton) wTabbedPane.getTabComponentAt(index);
final String basename = panel.getFilename() == null ? null : new File(panel.getFilename()).getName();
button.getLabel().setText(basename == null ? "* " + SNAPSHOT : basename);
button.setToolTipText(getTooltip(panel.getEnvironment(), panel.getFilename()));
}
private final void buildMenus() {
final JMenuBar wMenuBar = new JMenuBar();
setJMenuBar(wMenuBar);
buildFileMenu(wMenuBar);
buildSettingsMenu(wMenuBar);
wMenuBar.add(Box.createGlue());
buildHelpMenu(wMenuBar);
}
private void buildFileMenu(JMenuBar wMenuBar) {
final JMenu wMenu = new JMenu("File");
wMenuBar.add(wMenu);
final JMenuItem wOpen = new JMenuItem("Open");
wOpen.addActionListener(e -> open(wTabbedPane));
wMenu.add(wOpen);
}
private void buildSettingsMenu(JMenuBar wMenuBar) {
final JMenu wMenu = new JMenu("Settings");
// Refresh items when this menu is shown
wMenu.addMenuListener(new MenuListener() {
@Override
public void menuSelected(MenuEvent e) {
if (hasRunTimePanel()) {
// Warning : this code must be compliant with order of
// declarations of menus
final JMenu source = (JMenu) e.getSource();
refreshRefreshRate(source, 0);
refreshRendering(source, 1);
refreshDrawBorders(source, 2);
refreshShowStats(source, 3);
}
}
@Override
public void menuDeselected(MenuEvent e) {
// Ignore
}
@Override
public void menuCanceled(MenuEvent e) {
// Ignore
}
});
wMenuBar.add(wMenu);
buildRefreshRateMenu(wMenu);
buildRenderingMenu(wMenu);
buildDrawBordersMenuItem(wMenu);
buildShowStatsMenuItem(wMenu);
}
void refreshRefreshRate(JMenu source,
int pos) {
final RefreshRate rate = getRuntimePanel().getControlledChartPanel().getRefreshRate();
final JMenu menu = (JMenu) source.getItem(pos);
for (int index = 0; index < menu.getItemCount(); index++) {
final JRadioButtonMenuItem item = (JRadioButtonMenuItem) menu.getItem(index);
if (item.getActionCommand().equals(rate.name())) {
item.setSelected(true);
}
}
}
void refreshRendering(JMenu source,
int pos) {
final Rendering rendering = getRuntimePanel().getControlledChartPanel().getRendering();
final JMenu menu = (JMenu) source.getItem(pos);
for (int index = 0; index < menu.getItemCount(); index++) {
final JRadioButtonMenuItem item = (JRadioButtonMenuItem) menu.getItem(index);
if (item.getActionCommand().equals(rendering.name())) {
item.setSelected(true);
}
}
}
void refreshDrawBorders(JMenu source,
int pos) {
final boolean enabled = getRuntimePanel().getControlledChartPanel().getDrawBordersEnabled();
final JCheckBoxMenuItem item = (JCheckBoxMenuItem) source.getItem(pos);
item.setSelected(enabled);
}
void refreshShowStats(JMenu source,
int pos) {
final boolean enabled = getRuntimePanel().getControlledChartPanel().getDisplayStatsEnabled();
final JCheckBoxMenuItem item = (JCheckBoxMenuItem) source.getItem(pos);
item.setSelected(enabled);
}
private void buildRefreshRateMenu(JMenu wMenu) {
if (hasRunTimePanel()) {
wMenu.add(wRefreshRate);
final ButtonGroup group = new ButtonGroup();
for (final RefreshRate rate : RefreshRate.values()) {
final JRadioButtonMenuItem wMenuItem =
new JRadioButtonMenuItem(rate.getLabel() + " (" + rate.getDelay() + "ms)");
wMenuItem.setActionCommand(rate.name());
wRefreshRate.add(wMenuItem);
if (getRuntimePanel().getControlledChartPanel().getRefreshRate() == rate) {
wMenuItem.setSelected(true);
}
group.add(wMenuItem);
wMenuItem.addActionListener(e -> {
final String cmd = e.getActionCommand();
final RefreshRate lrate = RefreshRate.valueOf(cmd);
for (final EnvironmentPanel panel : getEnvironmentPanels()) {
panel.getControlledChartPanel().setRefreshRate(lrate);
}
});
}
}
}
private void buildRenderingMenu(JMenu wMenu) {
wMenu.add(wRendering);
final ButtonGroup group = new ButtonGroup();
for (final Rendering rendering : Rendering.values()) {
final JRadioButtonMenuItem wMenuItem = new JRadioButtonMenuItem(rendering.getLabel());
wMenuItem.setActionCommand(rendering.name());
wRendering.add(wMenuItem);
final boolean selected;
if (hasRunTimePanel()) {
selected = getRuntimePanel().getControlledChartPanel().getRendering() == rendering;
} else {
selected = rendering == Rendering.FASTEST;
}
wMenuItem.setSelected(selected);
group.add(wMenuItem);
wMenuItem.addActionListener(e -> {
final String cmd = e.getActionCommand();
final Rendering lrendering = Rendering.valueOf(cmd);
for (final EnvironmentPanel panel : getEnvironmentPanels()) {
panel.getControlledChartPanel().setRendering(lrendering);
}
});
}
}
private void buildDrawBordersMenuItem(JMenu wMenu) {
wMenu.add(wDrawBorders);
if (hasRunTimePanel()) {
wDrawBorders.setSelected(getRuntimePanel().getControlledChartPanel().getDrawBordersEnabled());
} else {
wDrawBorders.setSelected(true);
}
wDrawBorders.addActionListener(e -> {
final JCheckBoxMenuItem source = (JCheckBoxMenuItem) e.getSource();
for (final EnvironmentPanel panel : getEnvironmentPanels()) {
panel.getControlledChartPanel().setDrawBordersEnabled(source.isSelected());
}
});
}
private void buildShowStatsMenuItem(JMenu wMenu) {
wMenu.add(wShowStats);
if (hasRunTimePanel()) {
wShowStats.setSelected(getRuntimePanel().getControlledChartPanel().getDisplayStatsEnabled());
}
wShowStats.addActionListener(e -> {
final JCheckBoxMenuItem source = (JCheckBoxMenuItem) e.getSource();
for (final EnvironmentPanel panel : getEnvironmentPanels()) {
panel.getControlledChartPanel().setDisplayStatsEnabled(source.isSelected());
}
});
}
private void buildHelpMenu(JMenuBar wMenuBar) {
final JMenu wMenu = new JMenu("Help");
wMenuBar.add(wMenu);
final JMenuItem wMenuItem = new JMenuItem("About");
wMenuItem.addActionListener(e -> showAbout());
wMenu.add(wMenuItem);
}
protected void showAbout() {
if (wAbout == null) {
wAbout = new About(hasRunTimePanel() ? "CDC Perfs Monitor" : "CDC Perfs Viewer");
wAbout.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
wAbout = null;
}
});
final Rectangle selfBounds = getBounds();
final Rectangle aboutBounds = wAbout.getBounds();
wAbout.setLocation((int) (selfBounds.x + (selfBounds.getWidth() - aboutBounds.getWidth()) / 2),
(int) (selfBounds.y + (selfBounds.getHeight() - aboutBounds.getHeight()) / 2));
}
wAbout.setVisible(true);
}
protected void open(JComponent parent) {
final JFileChooser wChooser = new JFileChooser();
wChooser.setCurrentDirectory(openDir);
wChooser.setDialogTitle("Open");
for (final IoExtension ext : IoExtension.values()) {
if (ext.isSupported(IoMode.IMPORT)) {
final FileNameSuffixFilter filter =
new FileNameSuffixFilter(ext.getDescription() + " (*." + ext.getLabel() + ")", ext.getLabel());
wChooser.addChoosableFileFilter(filter);
}
}
final int result = wChooser.showOpenDialog(parent);
if (result == JFileChooser.APPROVE_OPTION) {
final SwingWorker worker = new SwingWorker() {
@Override
protected SnapshotEnvironment doInBackground() throws Exception {
final File file = wChooser.getSelectedFile();
final SwingProgressController controller = new SwingProgressController(parent, "Loading " + file.getPath());
final ProgressControllerInputStream is = new ProgressControllerInputStream(file, controller);
final SwingProgressController controller2 = new SwingProgressController(parent, "Analyzing " + file.getPath());
openDir = file.getParentFile();
return PerfsIo.load(is, file.getPath(), controller2);
}
@Override
protected void done() {
try {
final SnapshotEnvironment environment = get();
addSnaphot(environment, wChooser.getSelectedFile().getPath());
} catch (final ExecutionException e) {
LOGGER.catching(e);
final Throwable cause = e.getCause();
JOptionPane.showMessageDialog(parent,
"Failed to open '" + wChooser.getSelectedFile().getPath() + "'\n"
+ cause.getClass().getSimpleName() + " " + cause.getMessage(),
"Error",
JOptionPane.ERROR_MESSAGE);
} catch (final InterruptedException e) {
LOGGER.catching(e);
// Ignored at the moment as loading can not (yet) be cancelled
}
}
};
worker.execute();
}
}
}