package com.ibm.nmon.gui.interval;
import java.awt.BorderLayout;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BoxLayout;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import com.ibm.nmon.gui.GUIDialog;
import com.ibm.nmon.gui.Styles;
import com.ibm.nmon.gui.file.IntervalFileChooser;
import com.ibm.nmon.gui.main.NMONVisualizerGui;
import com.ibm.nmon.gui.time.SystemTimeList;
import com.ibm.nmon.gui.time.SystemTimeTableModel;
import com.ibm.nmon.gui.util.TimeZoneComboBox;
import com.ibm.nmon.interval.Interval;
import com.ibm.nmon.interval.IntervalListener;
import com.ibm.nmon.interval.IntervalManager;
import com.ibm.nmon.data.DataSet;
/**
* Dialog to allow adding and removing of Intervals.
*/
public final class IntervalManagerDialog extends GUIDialog implements IntervalListener {
private static final long serialVersionUID = 4964215301504994528L;
private final IntervalList intervals;
private final SystemTimeList systemTimes;
private final AbsoluteTimeIntervalPanel absolute;
private final RelativeTimeIntervalPanel relative;
private final BulkIntervalPanel bulk;
private final TimeZoneComboBox timeZones;
private final JButton save;
private IntervalFileChooser fileChooser;
public IntervalManagerDialog(NMONVisualizerGui gui) {
super(gui, gui.getMainFrame(), "Manage Intervals");
setLayout(new BoxLayout(getContentPane(), BoxLayout.PAGE_AXIS));
setIconImage(Styles.INTERVAL_ICON.getImage());
timeZones = new TimeZoneComboBox(gui.getDisplayTimeZone());
timeZones.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
IntervalManagerDialog.this.gui.setDisplayTimeZone(timeZones.getSelectedTimeZone());
}
});
JPanel timePanel = new JPanel();
JLabel label = new JLabel("Time Zone:");
label.setHorizontalAlignment(SwingConstants.TRAILING);
label.setFont(Styles.LABEL);
timePanel.add(label);
timePanel.add(timeZones);
AbstractAction loadAction = new AbstractAction() {
private static final long serialVersionUID = -8590641369327623520L;
@Override
public void actionPerformed(ActionEvent e) {
if (fileChooser == null) {
fileChooser = new IntervalFileChooser(IntervalManagerDialog.this.gui);
}
fileChooser.load();
}
};
JButton load = new JButton("Load");
load.addActionListener(loadAction);
AbstractAction saveAction = new AbstractAction() {
private static final long serialVersionUID = 8726615183618542317L;
@Override
public void actionPerformed(ActionEvent e) {
if (fileChooser == null) {
fileChooser = new IntervalFileChooser(IntervalManagerDialog.this.gui);
}
fileChooser.save();
}
};
save = new JButton("Save") {
private static final long serialVersionUID = 4862470165493725505L;
public void setEnabled(boolean b) {
// only associate keyboard shortcut when save is enabled
super.setEnabled(b);
if (b) {
((JPanel) getContentPane()).getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK), "save");
}
else {
((JPanel) getContentPane()).getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.remove(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK));
}
};
};
save.setIcon(Styles.SAVE_ICON);
save.addActionListener(saveAction);
JPanel buttons = new JPanel();
buttons.add(load);
buttons.add(save);
JPanel filler = new JPanel();
filler.setPreferredSize(buttons.getPreferredSize());
JPanel temp = new JPanel(new BorderLayout());
temp.add(filler, BorderLayout.LINE_START);
temp.add(timePanel, BorderLayout.CENTER);
temp.add(buttons, BorderLayout.LINE_END);
add(temp);
absolute = new AbsoluteTimeIntervalPanel(gui);
relative = new RelativeTimeIntervalPanel(gui);
bulk = new BulkIntervalPanel(gui);
JTabbedPane tabs = new JTabbedPane();
// note change the order here => change the hack in setVisible() for the listeners
tabs.add("Absolute", absolute);
tabs.add("Relative", relative);
tabs.add("Bulk", bulk);
tabs.setMnemonicAt(0, java.awt.event.KeyEvent.VK_B);
tabs.setMnemonicAt(1, java.awt.event.KeyEvent.VK_R);
tabs.setMnemonicAt(2, java.awt.event.KeyEvent.VK_A);
add(tabs);
intervals = new IntervalList(gui);
intervals.setBorder(Styles.DOUBLE_LINE_BORDER);
add(intervals);
systemTimes = new SystemTimeList(gui);
add(systemTimes);
tabs.addChangeListener(timeSynchronizer);
systemTimes.getTable().addMouseListener(systemTimesMenu);
intervals.getTable().addMouseListener(intervalsMenu);
// clean up internal listeners when this dialog closes
addHierarchyListener(intervals);
addHierarchyListener(systemTimes);
// when an interval is clicked or the start and end times are updated,
// update the system times
intervals.addPropertyChangeListener("interval", systemTimes);
// also update system times when the current data is changed
absolute.addPropertyChangeListener("interval", systemTimes);
relative.addPropertyChangeListener("interval", systemTimes);
bulk.addPropertyChangeListener("interval", systemTimes);
// keep the system times in sync with the current start and end times
KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener("focusOwner", focusManager);
JPanel content = (JPanel) getContentPane();
ActionMap actions = content.getActionMap();
InputMap inputs = content.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
actions.put("load", loadAction);
actions.put("save", saveAction);
inputs.put(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_DOWN_MASK), "load");
}
@Override
public void setVisible(boolean b) {
super.setVisible(b);
// ensure the start field for absolute requests focus
if (true) {
// hack to toggle state so the first interval panel actually has its listeners set
absolute.setEnabled(false);
absolute.setEnabled(true);
relative.setEnabled(false);
bulk.setEnabled(false);
save.setEnabled(gui.getIntervalManager().getIntervalCount() != 0);
gui.getIntervalManager().addListener(this);
}
// do not handle false; assume dialog is only opened once not opened and closed
}
@Override
public void dispose() {
super.dispose();
gui.getIntervalManager().removeListener(this);
KeyboardFocusManager.getCurrentKeyboardFocusManager().removePropertyChangeListener("focusOwner", focusManager);
// clean up listeners
absolute.setEnabled(false);
relative.setEnabled(false);
bulk.setEnabled(false);
}
@Override
public void intervalAdded(Interval interval) {
save.setEnabled(true);
}
@Override
public void intervalRemoved(Interval interval) {
save.setEnabled(gui.getIntervalManager().getIntervalCount() != 0);
}
@Override
public void intervalsCleared() {
save.setEnabled(false);
}
@Override
public void currentIntervalChanged(Interval interval) {
intervals.repaint();
}
@Override
public void intervalRenamed(Interval interval) {
intervals.repaint();
}
// display a menu on the system times table that allows the user to set the current interval or
// start and end times
private final MouseAdapter intervalsMenu = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
mouseReleased(e);
}
@Override
public void mouseReleased(MouseEvent e) {
int row = intervals.getTable().rowAtPoint(e.getPoint());
if (e.isPopupTrigger() && (row >= 0)) {
intervals.getTable().changeSelection(row, 0, false, false);
final Interval interval = ((IntervalTableModel) intervals.getTable().getModel()).getValueAt(row);
JPopupMenu menu = new JPopupMenu();
JMenuItem item = null;
item = new JMenuItem("Use as Start & End");
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
BaseIntervalPanel active = null;
if (absolute.isEnabled()) {
active = absolute;
}
else if (relative.isEnabled()) {
active = relative;
}
else if (bulk.isEnabled()) {
active = bulk;
}
if (active != null) {
active.setTimes(interval.getStart(), interval.getEnd());
active.revalidate();
}
}
});
menu.add(item);
if (!interval.equals(gui.getIntervalManager().getCurrentInterval())) {
item = new JMenuItem("Set as Current");
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
gui.getIntervalManager().setCurrentInterval(interval);
intervals.getTable().repaint();
}
});
menu.add(item);
}
menu.addSeparator();
item = new JMenuItem("Rename");
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
intervals.getTable().editCellAt(intervals.getTable().getSelectedRow(), 0);
}
});
menu.add(item);
menu.addSeparator();
item = new JMenuItem("Remove");
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
gui.getIntervalManager().removeInterval(interval);
intervals.getTable().repaint();
}
});
menu.add(item);
menu.show(e.getComponent(), e.getX(), e.getY());
// clear the selected system just like when the system times table loses focus
// this cannot be done in the focus listener since the interval table does not have
// to gain focus for the menu to show up
systemTimes.getTable().clearSelection();
}
}
};
// display a menu on the system times table that allows the user to set intervals or start and
// end times
private final MouseAdapter systemTimesMenu = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
mouseReleased(e);
}
@Override
public void mouseReleased(MouseEvent e) {
int row = systemTimes.getTable().rowAtPoint(e.getPoint());
if (e.isPopupTrigger() && (row >= 0)) {
systemTimes.getTable().changeSelection(row, 0, false, false);
final DataSet data = ((SystemTimeTableModel) systemTimes.getTable().getModel()).getValueAt(row);
JPopupMenu menu = new JPopupMenu();
JMenuItem item = new JMenuItem("Use as Start & End");
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
BaseIntervalPanel active = null;
if (absolute.isEnabled()) {
active = absolute;
}
else if (relative.isEnabled()) {
active = relative;
}
else if (bulk.isEnabled()) {
active = bulk;
}
if (active != null) {
active.setTimes(data.getStartTime(), data.getEndTime());
active.revalidate();
}
}
});
menu.add(item);
item = new JMenuItem("Add Interval");
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
long startTime = data.getStartTime();
long endTime = data.getEndTime();
NMONVisualizerGui gui = IntervalManagerDialog.this.gui;
// only add if not the default interval
if ((startTime != gui.getMinSystemTime()) || (endTime != gui.getMaxSystemTime())) {
IntervalManager intervalManager = gui.getIntervalManager();
// this interval should always be valid assuming the file times
// are valid
Interval i = new Interval(startTime, endTime);
if (intervalManager.addInterval(i)) {
firePropertyChange("interval", intervalManager.getCurrentInterval(), i);
intervalManager.setCurrentInterval(i);
}
gui.getIntervalManager().addInterval(new Interval(startTime, endTime));
}
}
});
menu.add(item);
menu.show(e.getComponent(), e.getX(), e.getY());
}
}
};
// when switching between absolute, relative and bulk, update the values so they stay in sync
private final ChangeListener timeSynchronizer = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
BaseIntervalPanel previous = null;
if (absolute.isEnabled()) {
previous = absolute;
}
else if (relative.isEnabled()) {
previous = relative;
}
else if (bulk.isEnabled()) {
previous = bulk;
}
else {
return;
}
JTabbedPane tabs = (JTabbedPane) e.getSource();
BaseIntervalPanel current = (BaseIntervalPanel) tabs.getSelectedComponent();
if (current != null) {
current.setIntervalName(previous.getIntervalName());
current.setTimes(previous.getStartTime(), previous.getEndTime());
current.setEnabled(true);
}
// remove listeners
previous.setEnabled(false);
current.propertyChange(new PropertyChangeEvent(this, "timeZone", null, previous.getTimeZone()));
}
};
// when focus returns to the time panels,
// set the system times interval to the start and end times
private final PropertyChangeListener focusManager = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue() != null) {
// absolute parent => tabs
// if any of the data entry forms are selected, unselect the interval table
if (SwingUtilities.isDescendingFrom((java.awt.Component) evt.getNewValue(), absolute.getParent())) {
intervals.getTable().clearSelection();
systemTimes.getTable().clearSelection();
}
else if (intervals.getTable() == evt.getNewValue()) {
systemTimes.getTable().clearSelection();
}
}
}
};
}