package nbtool.gui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.nio.file.FileStore;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import javax.swing.AbstractAction;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import nbtool.data.OrderedSet;
import nbtool.data.ViewProfile;
import nbtool.data.group.AllGroups;
import nbtool.data.group.Group;
import nbtool.data.log.Log;
import nbtool.data.log.LogReference;
import nbtool.data.log.LogSearching;
import nbtool.data.log.LogSearching.Criteria;
import nbtool.data.log.LogSorting;
import nbtool.gui.logdnd.LogDND;
import nbtool.gui.logdnd.LogDND.LogDNDSource;
import nbtool.gui.logdnd.LogDND.LogDNDTarget;
import nbtool.gui.utilitypanes.UtilityManager;
import nbtool.gui.utilitypanes.UtilityParent;
import nbtool.io.CommonIO.IOFirstResponder;
import nbtool.io.CommonIO.IOInstance;
import nbtool.nio.CrossServer.CrossInstance;
import nbtool.nio.FileIO;
import nbtool.nio.LogRPC;
import nbtool.nio.RobotConnection;
import nbtool.nio.RobotConnection.RobotFlag;
import nbtool.util.Center;
import nbtool.util.Center.NBToolShutdownListener;
import nbtool.util.Debug;
import nbtool.util.Events;
import nbtool.util.Events.ViewProfileSetChanged;
import nbtool.util.SharedConstants;
import nbtool.util.ToolSettings;
import nbtool.util.UserSettings;
import nbtool.util.UserSettings.DisplaySettings;
import nbtool.util.Utility;
import nbtool.util.Utility.Pair;
public class ToolDisplayHandler implements IOFirstResponder, Events.LogsFound, Events.LogRefsFound, Events.GroupAdded {
private final long id = Utility.getNextIndex(this);
private final ToolDisplayHandler outerThis = this;
private static final Debug.DebugSettings debug = Debug.createSettings(Debug.INFO);
@Override
public String toString() {
return this.getClass().getSimpleName() + "-" + id;
}
protected ToolDisplayHandler() {
ToolMessage.displayInfo("creating new display and handler: %s", this);
display = new ToolDisplay();
listener = new TitleListener();
setupUtilitiesTab();
setupControlTab();
setupLogsTab();
setupLogDisplay();
setupFooter();
Center.listen(Events.LogsFound.class, this, true);
Center.listen(Events.LogRefsFound.class, this, true);
Center.listen(Events.GroupAdded.class, this, true);
final String boundsKey = this.toString();
Center.listen(new NBToolShutdownListener() {
@Override
public void nbtoolShutdownCallback() {
DisplaySettings end = new DisplaySettings(display.getBounds(), viewProfile,
display.topLevelSplit.getDividerLocation());
UserSettings.BOUNDS_MAP.put(boundsKey, end);
}
});
DisplaySettings ds = UserSettings.BOUNDS_MAP.get(boundsKey);
if (ds != null) {
display.setBounds(ds.bounds);
display.topLevelSplit.setDividerLocation(ds.splitLocation);
viewProfile = ds.profile == null ? ViewProfile.DEFAULT_PROFILE : ds.profile;
}
display.setTitle("nbtool v" + ToolSettings.VERSION + "." + ToolSettings.MINOR_VERSION);
display.setMinimumSize(MIN_SIZE);
if (id == 0) {
display.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
} else {
display.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
}
setupKeyBindings();
display.topLevelSplit.setContinuousLayout(false);
display.topLevelSplit.setAutoscrolls(false);
Dimension min = ToolSettings.DEFAULT_BOUNDS.getSize();
min.width = 0;
display.leftSideTabs.setMinimumSize(min);
display.displayTabs.setMinimumSize(min);
display.topLevelSplit.requestFocus();
}
public void show(boolean vis) {
display.setVisible(vis);
}
public boolean isVisible() {
return display.isVisible();
}
// ^^ EXTERNAL
// ------------------------------------------
// vv INTERNAL
private static final Dimension MIN_SIZE = new Dimension(800, 600);
private final ToolDisplay display;
private RobotConnection robot = null;
private Group lastGroup = null;
private ViewProfile viewProfile = ViewProfile.DEFAULT_PROFILE;
private static <T> void updateComboBoxAndSettings(JComboBox<T> box, OrderedSet<T> set, T newest) {
if (newest != null)
set.update(newest);
box.setModel(new DefaultComboBoxModel<T>(set.vector()));
if (newest != null)
box.setSelectedItem(newest);
else if (set.vector().isEmpty()) {
box.setSelectedIndex(-1);
} else {
box.setSelectedIndex(0);
}
}
private void setupKeyBindings() {
AbstractAction switchTabsAction = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
char key = e.getActionCommand().charAt(0);
if (Character.isDigit(key)) {
int n = (Character.getNumericValue(key) - 1);
if (n >= 0 && n < display.displayTabs.getTabCount()) {
display.displayTabs.setSelectedIndex(n);
}
return;
}
}
};
AbstractAction loadAction = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
if (display.leftSideTabs.getSelectedComponent() == display.controlTab)
display.loadButton.doClick();
}
};
AbstractAction toggleLeftSideAction = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
if (display.topLevelSplit.getDividerLocation() > 10) {
display.topLevelSplit.setDividerLocation(0);
} else {
display.topLevelSplit.setDividerLocation(0.5);
display.topLevelSplit.requestFocus();
}
}
};
display.topLevelSplit.getActionMap().put("switchTabsAction", switchTabsAction);
display.topLevelSplit.getActionMap().put("loadAction", loadAction);
display.topLevelSplit.getActionMap().put("toggleLeftSideAction", toggleLeftSideAction);
for (char c = '1'; c <= '9'; ++c) {
display.topLevelSplit.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(c),
"switchTabsAction");
}
display.topLevelSplit.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "loadAction");
display.topLevelSplit.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke('`'),
"toggleLeftSideAction");
}
private void setupViewProfileBox() {
Collection<ViewProfile> set = ViewProfile.PROFILES.values();
display.viewProfileBox.setModel(new DefaultComboBoxModel<>(set.toArray(new ViewProfile[0])));
if (set.contains(viewProfile)) {
display.viewProfileBox.setSelectedItem(viewProfile);
} else {
ViewProfile def = ViewProfile.DEFAULT_PROFILE;
display.viewProfileBox.setSelectedItem(def);
viewProfile = def;
}
}
private void setupUtilitiesTab() {
display.debugLevelBox.setModel(new DefaultComboBoxModel<Debug.LogLevel>(Debug.LogLevel.values()));
display.debugLevelBox.setEditable(false);
display.debugLevelBox.setSelectedIndex(Arrays.asList(Debug.LogLevel.values()).indexOf(Debug.level));
display.debugLevelBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int sel = display.debugLevelBox.getSelectedIndex();
Debug.level = Debug.LogLevel.values()[sel];
UserSettings.logLevel = Debug.level;
Debug.print("changed Debug.level to: %s", Debug.level);
}
});
setupViewProfileBox();
display.viewProfileBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ViewProfile prof = (ViewProfile) display.viewProfileBox.getSelectedItem();
if (prof != null) {
viewProfile = prof;
}
}
});
Center.listen(Events.ViewProfileSetChanged.class, new ViewProfileSetChanged() {
@Override
public void viewProfileSetChanged(Object src) {
setupViewProfileBox();
}
}, true);
display.venueField.setText(UserSettings.venue);
display.venueField.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
UserSettings.venue = display.venueField.getText();
}
});
JPanel utilityHolder = new JPanel();
// utilityHolder.setLayout(new BoxLayout(utilityHolder,
// BoxLayout.Y_AXIS));
utilityHolder.setLayout(new GridLayout(0, 1));
for (UtilityParent up : UtilityManager.utilities) {
utilityHolder.add(new UtilityPanel(up));
}
// utilityHolder.add(Box.createVerticalGlue());
display.utilityScrollPane.setViewportView(utilityHolder);
}
private void controlSelectAction() {
Path selected = PathChooser.chooseDirPath(display, null);
if (selected != null) {
if (FileIO.isValidLogFolder(selected)) {
updateComboBoxAndSettings(display.pathBox, UserSettings.loadPathes, selected);
} else {
ToolMessage.displayError("invalid path to logs: %s", selected);
}
}
}
private void controlLoadAction() {
if (robot == null) {
Path selected = (Path) display.pathBox.getSelectedItem();
if (selected == null) {
Debug.error("null path");
ToolMessage.display("load action: null path", Color.RED);
return;
}
if (FileIO.isValidLogFolder(selected)) {
updateComboBoxAndSettings(display.pathBox, UserSettings.loadPathes, selected);
lastGroup = Group.groupFromPath(selected);
LogReference[] added;
try {
added = FileIO.readAllRefsFromPath(selected, true);
} catch (Throwable e) {
ToolMessage.displayError("error {%s} (see below) reading Log refs from %s", e.getMessage(),
selected);
e.printStackTrace();
return;
}
lastGroup.add(added);
// Log[] addedLogs = new Log[added.length];
// for (int i = 0; i < added.length; ++i)
// addedLogs[i] = added[i].get();
ToolMessage.displayInfo("loaded %d logs into %s", added.length, lastGroup);
Events.GGroupAdded.generate(this, lastGroup);
Events.GLogRefsFound.generate(this, added);
// Events.GLogsFound.generate(this, addedLogs);
display.leftSideTabs.setSelectedComponent(display.logTab);
} else {
Debug.error("invalid Log folder: " + selected.toString());
ToolMessage.display("invalid Log folder: " + selected.toString(), Color.RED);
return;
}
} else {
Debug.error("cannot load session while streaming (%s)", robot);
ToolMessage.display("cannot load session while streaming", Color.ORANGE);
}
}
private void controlRequestFlags() {
LogRPC.requestFlags(new IOFirstResponder() {
@Override
public void ioFinished(IOInstance instance) {
assert (false);
}
@Override
public void ioReceived(IOInstance inst, int ret, Log... out) {
if (inst != robot) {
Debug.error("got requestFlags return from instance %s, expected from %s!", inst, robot);
} else {
assert (out.length == 1);
Log flags = out[0];
assert (flags.logClass.equals(SharedConstants.LogClass_Flags()));
RobotFlag[] parsedFlags = RobotFlag.parseLog(flags);
JPanel container = new JPanel();
container.setLayout(new GridLayout(parsedFlags.length, 1));
for (RobotFlag val : parsedFlags) {
container.add(new FlagPanel(robot, val));
}
container.setMinimumSize(container.getPreferredSize());
display.rpcScrollPane.setViewportView(container);
}
}
@Override
public boolean ioMayRespondOnCenterThread(IOInstance inst) {
return false;
}
}, robot);
}
private class ControlConnectRunnable extends Center.EventRunnable {
private final String robotAddress;
ControlConnectRunnable(String s) {
this.robotAddress = s;
}
@Override
protected void run() {
Debug.warn("trying to connect to %s", robotAddress);
robot = RobotConnection.connectToRobot(robotAddress, outerThis);
if (robot == null) {
ToolMessage.displayError("connection failed to: %s", robotAddress);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
display.connectButton.setEnabled(true);
}
});
return;
} else {
ToolMessage.displayWarn("SUCCESS: connected to %s (%s)", robotAddress, robot);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
display.connectButton.setEnabled(true);
display.connectButton.setText("disconnect");
display.loadButton.setEnabled(false);
updateComboBoxAndSettings(display.robotAddressBox, UserSettings.addresses, robotAddress);
controlRequestFlags();
lastGroup = Group.groupForStream(robotAddress);
Events.GGroupAdded.generate(this, lastGroup);
}
});
}
}
}
private void controlConnectAction() {
if (robot == null) {
assert (display.connectButton.getText().equals("connect"));
} else {
assert (display.connectButton.getText().equals("disconnect"));
Debug.info("trying to kill %s", robot);
final RobotConnection _robot = robot;
Center.addEvent(new Center.EventRunnable() {
@Override
protected void run() {
_robot.kill();
}
});
return;
}
String address = (String) display.robotAddressBox.getSelectedItem();
if (address == null) {
ToolMessage.displayError("choose valid address");
return;
}
address = address.trim();
if (display.localCheckBox.isSelected() && !address.endsWith(".local")) {
address += ".local";
}
display.connectButton.setEnabled(false);
Center.addEvent(new ControlConnectRunnable(address));
}
private void setupKeepSlider() {
Hashtable<Integer, JLabel> labelTable = new Hashtable<>();
labelTable.put(new Integer(0), new JLabel("0.0"));
labelTable.put(new Integer(1), new JLabel("0.01"));
labelTable.put(new Integer(2), new JLabel("0.1"));
labelTable.put(new Integer(3), new JLabel("0.2"));
labelTable.put(new Integer(4), new JLabel("1.0"));
display.keepSlider.setSnapToTicks(true);
display.keepSlider.setMinimum(0);
display.keepSlider.setMaximum(labelTable.size() - 1);
display.keepSlider.setLabelTable(labelTable);
display.keepSlider.setPaintLabels(true);
display.keepSlider.setPaintTicks(true);
display.keepSlider.setValue(labelTable.size() - 1);
}
private int keepMod() {
int val = display.keepSlider.getValue();
switch (val) {
case 0:
return 0;
case 1:
return 100;
case 2:
return 10;
case 3:
return 5;
case 4:
return 1;
default:
debug.error("bad keepSlider value: %d", val);
return -1;
}
}
private boolean shouldKeep(long index) {
int km = keepMod();
return (km > 0) && ((index % km) == 0);
}
@SuppressWarnings("unchecked")
private void setupControlTab() {
updateComboBoxAndSettings(display.pathBox, UserSettings.loadPathes, null);
updateComboBoxAndSettings(display.robotAddressBox, UserSettings.addresses, null);
final String last = "don't stream";
Vector<String> streamVector = new Vector<String>();
for (Pair<String, Criteria> pair : LogSearching.CRITERIA) {
streamVector.add("stream: " + pair.a);
}
streamVector.add(last);
display.streamComboBox.setModel(new DefaultComboBoxModel<String>(streamVector));
display.streamComboBox.setSelectedItem(last);
display.selectButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
controlSelectAction();
}
});
display.loadButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
controlLoadAction();
}
});
display.connectButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
controlConnectAction();
}
});
setupKeepSlider();
// Stop streaming if the user selects a log...
Center.listen(Events.LogSelected.class, new Events.LogSelected() {
@Override
public void logSelected(Object source, Log first, List<Log> alsoSelected) {
debug.info("Log{%s} selected, %s disabling streaming!", first, outerClassThis);
display.streamComboBox.setSelectedItem(last);
}
}, true);
display.leftSideTabs.setSelectedComponent(display.controlTab);
;
}
private ArrayList<LogReference> currentlyDisplayedFrom(Group group) {
ArrayList<LogReference> refs = new ArrayList<>((group).logs);
LogSearching.Criteria criteria = LogSearching.criteriaAt(display.sasStreamProfile.getSelectedIndex());
if (criteria != null) {
for (int i = 0; i < refs.size();) {
if (!criteria.fits(refs.get(i).description))
refs.remove(i);
else
++i;
}
}
LogSorting.Sort sort = (LogSorting.Sort) display.sasOrderBox.getSelectedItem();
if (sort != null) {
Collections.sort(refs, sort.comparator);
}
return refs;
}
private boolean resemblesGeneratedFilename(String name, LogReference ref) {
String[] checks = new String[] { String.format("%s_%s_v%d", ref.host_name, ref.logClass, ToolSettings.VERSION),
String.format("temp_log_id"), String.format("log_%s_", ref.logClass) };
for (String check : checks) {
if (name.startsWith(check))
return true;
}
return false;
}
private class LogTreeModel implements TreeModel, TreeSelectionListener, LogDNDSource, TreeCellRenderer {
private final DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded,
boolean leaf, int row, boolean hasFocus) {
JLabel rendered = (JLabel) renderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row,
hasFocus);
if (value instanceof Group) {
Group group = (Group) value;
rendered.setText(group.guiString());
} else if (value instanceof LogReference) {
LogReference reference = (LogReference) value;
if (!reference.temporary()) {
// rendered.setText(reference.loadPath().getFileName().toString());
// rendered.setText("" + reference.loadPath().getFileName().toString().length());
Path lp = reference.loadPath();
String last = lp.getFileName().toString();
if (last.length() < 20 || !resemblesGeneratedFilename(last, reference)) {
rendered.setText(last);
} else {
rendered.setText(reference.guiString());
}
} else {
rendered.setText(reference.guiString());
}
}
return rendered;
}
@Override
public void valueChanged(TreeSelectionEvent e) {
if (!e.isAddedPath()) {
// path unselected, ignore
return;
}
TreePath[] selected = display.logTree.getSelectionPaths();
switch (selected[0].getPathCount()) {
case 0:
Debug.error("null selection!");
break;
case 1:
Debug.error("root selected!");
break;
case 2: {
Group group = (Group) selected[0].getLastPathComponent();
displayGroup(group);
Events.GGroupSelected.generate(this, group);
}
return;
case 3: {
List<Log> all = new LinkedList<>();
for (TreePath path : selected) {
if (path.getPathCount() != 3)
continue; // it isn't a Log selection
LogReference ref = (LogReference) path.getLastPathComponent();
all.add(ref.get());
}
Log first = all.remove(0);
displayLog(first, all);
Events.GLogSelected.generate(outerClassThis, first, all);
}
return;
default:
Debug.error("selection count %d %s", selected[0].getPathCount(), selected[0].getLastPathComponent());
}
}
@Override
public Object getRoot() {
return this;
}
@Override
public Object getChild(Object parent, int index) {
if (parent == this) {
return AllGroups.get(index);
} else if (parent instanceof Group) {
Group group = (Group) parent;
return currentlyDisplayedFrom(group).get(index);
} else
return null;
}
@Override
public int getChildCount(Object parent) {
if (parent == this) {
return AllGroups.getGroupCount();
} else if (parent instanceof Group) {
return currentlyDisplayedFrom((Group) parent).size();
} else {
return 0;
}
}
@Override
public boolean isLeaf(Object node) {
return node.getClass() == LogReference.class;
}
@Override
public void valueForPathChanged(TreePath path, Object newValue) {
Debug.error("logTree should not be editable...");
}
@Override
public int getIndexOfChild(Object parent, Object child) {
if (parent == this) {
return AllGroups.allGroups.indexOf(child);
} else if (parent instanceof Group) {
return currentlyDisplayedFrom((Group) parent).indexOf(child);
} else {
Debug.error("parent %s not container!", parent);
return -1;
}
}
private final ArrayList<TreeModelListener> listeners = new ArrayList<TreeModelListener>();
@Override
public void addTreeModelListener(TreeModelListener l) {
listeners.add(l);
}
@Override
public void removeTreeModelListener(TreeModelListener l) {
listeners.remove(l);
}
@Override
public Log[] supplyLogsForDrag() {
ArrayList<Log> refs = new ArrayList<>();
for (TreePath tp : display.logTree.getSelectionPaths()) {
if (tp.getPathCount() == 3) {
LogReference reference = (LogReference) tp.getLastPathComponent();
refs.add(reference.get());
}
}
return refs.toArray(new Log[0]);
}
protected void deleteCurrent() {
int num_logs_deleted = 0;
Set<Group> groups_changed = new HashSet<>();
for (TreePath tp : display.logTree.getSelectionPaths()) {
if (tp.getPathCount() == 3) {
Group group = (Group) tp.getPath()[1];
LogReference reference = (LogReference) tp.getLastPathComponent();
Debug.warn("deleting {%s} from {%s}", reference, group);
group.remove(reference);
++num_logs_deleted;
groups_changed.add(group);
} else {
Debug.warn("cannot delete {%s}", tp.getLastPathComponent());
}
}
for (Group group : groups_changed) {
TreeModelEvent removeEvent = new TreeModelEvent(this, new Object[] { this, group });
for (TreeModelListener listener : listeners)
listener.treeStructureChanged(removeEvent);
}
ToolMessage.displayAndPrint("deleted %d logs", num_logs_deleted);
}
protected void showGroupAdded(Group group) {
// TreeModelEvent tme = new TreeModelEvent(this, new Object[]{root},
// new int[]{AllGroups.allGroups.indexOf(group)},
// new Object[]{group});
Debug.event("LogModel group added");
TreeModelEvent changed = new TreeModelEvent(this, new Object[] { this });
for (TreeModelListener listener : listeners)
listener.treeStructureChanged(changed);
}
protected void showReferenceAdded(LogReference reference) {
Debug.event("LogModel reference added");
TreeModelEvent tme = new TreeModelEvent(this, new Object[] { this, reference.container });
for (TreeModelListener l : listeners) {
l.treeStructureChanged(tme);
}
}
protected void treeReSorted() {
Debug.event("LogModel tree resorted");
TreeModelEvent tme = new TreeModelEvent(this, new Object[] { this });
for (TreeModelListener l : listeners) {
l.treeStructureChanged(tme);
}
}
}
private LogTreeModel model;
private final ToolDisplayHandler outerClassThis = this;
private void setupLogsTab() {
String last = "don't filter";
Vector<String> streamVector = new Vector<String>();
for (Pair<String, Criteria> pair : LogSearching.CRITERIA) {
streamVector.add("search for: " + pair.a);
}
streamVector.add(last);
ActionListener tellModel = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
model.treeReSorted();
}
};
display.sasStreamProfile.setModel(new DefaultComboBoxModel<String>(streamVector));
display.sasStreamProfile.setSelectedItem(last);
display.sasStreamProfile.addActionListener(tellModel);
display.sasOrderBox.setModel(new DefaultComboBoxModel<>(LogSorting.Sort.values()));
display.sasOrderBox.setSelectedItem(LogSorting.Sort.BY_ARRIVAL);
display.sasOrderBox.addActionListener(tellModel);
model = new LogTreeModel();
display.logTree.setModel(model);
display.logTree.setCellRenderer(model);
display.logTree.setEditable(false);
display.logTree.setRootVisible(false);
display.logTree.setScrollsOnExpand(true);
display.logTree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
display.logTree.addTreeSelectionListener(model);
LogDND.makeComponentSource(display.logTree, model);
display.logTree.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_DELETE || e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
model.deleteCurrent();
}
}
});
}
private void displayLog(Log log) {
displayLog(log, new LinkedList<Log>());
}
private void displayLog(Log log, List<Log> also) {
tabDisplayer.setContents(viewProfile, log, also);
}
private void displayGroup(Group g) {
tabDisplayer.setContents(g);
}
private ToolDisplayTabs tabDisplayer = null;
private void setupLogDisplay() {
LogDND.makeComponentTarget(display.displayTabs, new LogDNDTarget() {
@Override
public void takeLogsFromDrop(Log[] log) {
if (log.length < 1)
return;
List<Log> list = new LinkedList<>(Arrays.asList(log));
displayLog(list.remove(0), list);
}
});
tabDisplayer = new ToolDisplayTabs(display);
}
TitleListener listener = null;
class TitleListener implements Events.CrossStatus, Events.RobotConnectionStatus {
TitleListener() {
Debug.event("TitleListener() listening...");
Center.listen(Events.CrossStatus.class, this, true);
Center.listen(Events.RobotConnectionStatus.class, this, true);
}
int numCross = 0;
int numRobot = 0;
@Override
public void robotStatus(RobotConnection inst, boolean up) {
numRobot += up ? 1 : -1;
set();
}
@Override
public void nbCrossFound(CrossInstance inst, boolean up) {
numCross += up ? 1 : -1;
set();
}
private void set() {
display.setTitle(String.format("nbtool - %d CrossInstance, %d RobotConnection", numCross, numRobot));
}
}
private Timer footerJvmTimer = null;
private Timer footerDiskTimer = null;
private final int progressSize = 50;
private FileStore usedFileStore = null;
private void setupFooter() {
try {
usedFileStore = Files.getFileStore(ToolSettings.NBITES_DIR_PATH);
} catch (Exception e) {
e.printStackTrace();
;
throw new Error(e);
}
Debug.warn("Tool footer using fileStore: %s", usedFileStore.name());
display.diskAvailLabel.setText(Utility.progressString(progressSize, 0.5));
display.jvmAvailLabel.setText(Utility.progressString(progressSize, 0.5));
footerJvmTimer = new Timer(1000, // ms
new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
footerJvmAction();
}
});
footerJvmTimer.start();
footerDiskTimer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
footerDiskAction();
}
});
footerDiskTimer.start();
}
private void footerJvmAction() {
long jvmUsed = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();
long jvmMax = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getMax();
double jvmFrac = ((double) jvmUsed) / ((double) jvmMax);
display.jvmAvailLabel.setText(Utility.progressString(progressSize, jvmFrac));
//
display.footerExtraLabel.setText(String.format(" %d logs, %sused, %smax", AllGroups.getLogCount(),
Utility.byteString(jvmUsed, true, true, false, false),
Utility.byteString(jvmMax, true, true, false, false)));
// Debug.print("jvm %d %s", jvmFrac,
// String.format("%d logs, %s used memory, %s max",
// AllGroups.getLogCount(), Utility.byteString(jvmUsed, true, true,
// false, false),
// Utility.byteString(jvmMax, true, true, false, false))
// );
}
private void footerDiskAction() {
long diskSpace;
long diskUsed;
try {
diskSpace = usedFileStore.getTotalSpace();
diskUsed = diskSpace - usedFileStore.getUnallocatedSpace();
} catch (IOException e) {
e.printStackTrace();
throw new Error(e);
}
double diskFrac = ((double) diskUsed) / ((double) diskSpace);
display.diskAvailLabel.setText(Utility.progressString(progressSize, diskFrac));
}
/*
* Events interface implementations...
*/
@Override
public void groupAdded(Object source, Group group) {
model.showGroupAdded(group);
}
@Override
public void logsFound(Object source, Log... found) {
for (Log log : found) {
LogReference ref = log.getReference();
if (ref != null) {
model.showReferenceAdded(ref);
}
}
}
@Override
public void logRefsFound(Object source, LogReference... found) {
for (LogReference ref : found) {
model.showReferenceAdded(ref);
}
}
/*
* IO implementation...
*/
@Override
public void ioFinished(IOInstance instance) {
if (instance == robot) {
ToolMessage.displayWarn("robot %s disconnected!", instance);
display.rpcScrollPane.setViewportView(new JLabel("no connection"));
display.connectButton.setText("connect");
display.loadButton.setEnabled(true);
robot = null;
} else {
Debug.error("informed of dead connection %s but robot=%s", instance, robot);
}
}
@Override
public void ioReceived(IOInstance inst, int ret, Log... out) {
if (inst == robot) {
// Deal with streaming...
LogSearching.Criteria criteria = LogSearching.criteriaAt(display.streamComboBox.getSelectedIndex());
if (criteria != null) {
Log pass = null;
for (Log l : out) {
if (criteria.fits(l.getFullDescription()))
pass = l;
}
if (pass != null)
displayLog(pass);
}
// Deal with adding to groups (i.e. keeping)...
assert (lastGroup != null);
for (Log l : out) {
if (shouldKeep(l.jvm_unique_id))
lastGroup.add(LogReference.referenceFromLog(l));
}
Events.GLogsFound.generate(this, out);
} else {
Debug.error("%s got %d surprising logs from %s", this, out.length, inst);
}
}
@Override
public boolean ioMayRespondOnCenterThread(IOInstance inst) {
return false;
}
}