edu.cmu.tetradapp.app.SessionEditorToolbar Maven / Gradle / Ivy
///////////////////////////////////////////////////////////////////////////////
// For information as to what this class does, see the Javadoc, below. //
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, //
// 2007, 2008, 2009, 2010, 2014, 2015, 2022 by Peter Spirtes, Richard //
// Scheines, Joseph Ramsey, and Clark Glymour. //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation; either version 2 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program; if not, write to the Free Software //
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //
///////////////////////////////////////////////////////////////////////////////
package edu.cmu.tetradapp.app;
import edu.cmu.tetradapp.util.ImageUtils;
import edu.cmu.tetradapp.workbench.AbstractWorkbench;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;
/**
* Displays a vertical list of buttons that determine the next action the user can take in the session editor workbench,
* whether it's selecting and moving a node, adding a node of a particular type, or adding an edge.
*
* @author josephramsey
* @see SessionEditor
* @see SessionEditorToolbar
*/
final class SessionEditorToolbar extends JPanel {
/**
* The node type of the button that is used for the Select/Move tool.
*/
private final String selectType = "Select";
private final String edgeSelectType = "Edge";
/**
* \ The map from JToggleButtons to String node types.
*/
private final Map nodeTypes = new HashMap<>();
/**
* The workbench this toolbar controls.
*/
private final SessionEditorWorkbench workbench;
/**
* True iff the toolbar is responding to events.
*/
private boolean respondingToEvents = true;
/**
* True iff the shift key was down on last click.
*/
private boolean shiftDown;
/**
* Constructs a new session toolbar.
*
* @param workbench the workbench this toolbar controls.
*/
public SessionEditorToolbar(SessionEditorWorkbench workbench) {
if (workbench == null) {
throw new NullPointerException("Workbench must not be null.");
}
this.workbench = workbench;
// Set up panel.
Box buttonsPanel = Box.createVerticalBox();
// buttonsPanel.setBackground(new Color(198, 232, 252));
buttonsPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
// Create buttons.
/*
Node infos for all of the nodes.
*/
ButtonInfo[] buttonInfos = {
new ButtonInfo("Select", "Select and Move", "move",
"Select and move nodes or groups of nodes "
+ "
on the workbench."),
new ButtonInfo("Edge", "Draw Edge", "flow",
"Add an edge from one node to another to declare"
+ "
that the object in the first node should be used "
+ "
to construct the object in the second node."
+ "
As a shortcut, hold down the Control key."
+ ""),
new ButtonInfo("Graph", "Graph", "graph", "Add a graph node."),
new ButtonInfo("Compare", "Compare", "compare",
"Add a node to compare graphs or SEM IM's."),
new ButtonInfo("PM", "Parametric Model", "pm",
"Add a node for a parametric model."),
new ButtonInfo("IM", "Instantiated Model", "im",
"Add a node for an instantiated model."),
new ButtonInfo("Estimator", "Estimator", "estimator",
"Add a node for an estimator."),
new ButtonInfo("Data", "Data", "data",
"Add a node for a data object."),
new ButtonInfo("Simulation", "Simulation", "simulation",
"Add a node for a simulation object."),
new ButtonInfo("Search", "Search", "search",
"Add a node for a search algorithm."),
new ButtonInfo("Knowledge", "Knowledge", "knowledge", "Add a knowledge box node."),
new ButtonInfo("Updater", "Updater", "updater",
"Add a node for an updater."),
//new ButtonInfo("Classify", "Classify", "search",
//"Add a node for a classifier."),
new ButtonInfo("Regression", "Regression", "regression",
"Add a node for a regression."),
new ButtonInfo("Note", "Note", "note",
"Add a note to the session.")
};
JToggleButton[] buttons = new JToggleButton[buttonInfos.length];
for (int i = 0; i < buttonInfos.length; i++) {
buttons[i] = constructButton(buttonInfos[i]);
}
// Add all buttons to a button group.
ButtonGroup buttonGroup = new ButtonGroup();
for (int i = 0; i < buttonInfos.length; i++) {
buttonGroup.add(buttons[i]);
}
// This seems to be fixed. Now creating weirdness. jdramsey 3/4/2014
// // Add a focus listener to help buttons not deselect when the
// // mouse slides away from the button.
// FocusListener focusListener = new FocusAdapter() {
// public void focusGained(FocusEvent e) {
// JToggleButton component = (JToggleButton) e.getComponent();
// component.getModel().setSelected(true);
// }
// };
//
// for (int i = 0; i < buttonInfos.length; i++) {
// buttons[i].addFocusListener(focusListener);
// }
// Add an action listener to help send messages to the
// workbench.
ChangeListener changeListener = e -> {
JToggleButton _button = (JToggleButton) e.getSource();
if (_button.getModel().isSelected()) {
setWorkbenchMode(_button);
// setCursor(workbench.getCursor());
}
};
for (int i = 0; i < buttonInfos.length; i++) {
buttons[i].addChangeListener(changeListener);
}
// Select the Select button.
JToggleButton button = getButtonForType(this.selectType);
assert button != null;
button.getModel().setSelected(true);
// Add the buttons to the workbench.
for (int i = 0; i < buttonInfos.length; i++) {
buttonsPanel.add(buttons[i]);
buttonsPanel.add(Box.createVerticalStrut(5));
}
// Put the panel in a scrollpane.
this.setLayout(new BorderLayout());
JScrollPane scroll = new JScrollPane(buttonsPanel);
scroll.setPreferredSize(new Dimension(130, 1000));
add(scroll, BorderLayout.CENTER);
// Add property change listener so that selection can be moved
// back to "SELECT_MOVE" after an action.
workbench.addPropertyChangeListener(e -> {
if (!isRespondingToEvents()) {
return;
}
String propertyName = e.getPropertyName();
if ("nodeAdded".equals(propertyName)) {
if (!isShiftDown()) {
resetSelectMove();
}
} else if ("edgeAdded".equals(propertyName)) {
// keep edge select type selected
JToggleButton selectButton = getButtonForType(SessionEditorToolbar.this.edgeSelectType);
assert selectButton != null;
if (!(selectButton.isSelected())) {
selectButton.doClick();
selectButton.requestFocus();
}
}
});
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addKeyEventDispatcher(e -> {
int keyCode = e.getKeyCode();
int id = e.getID();
if (keyCode == KeyEvent.VK_SHIFT) {
if (id == KeyEvent.KEY_PRESSED) {
setShiftDown(true);
} else if (id == KeyEvent.KEY_RELEASED) {
setShiftDown(false);
resetSelectMove();
}
}
return false;
});
resetSelectMove();
}
/**
* Sets the selection back to move/select.
*/
private void resetSelectMove() {
JToggleButton selectButton = getButtonForType(this.selectType);
assert selectButton != null;
if (!(selectButton.isSelected())) {
selectButton.doClick();
selectButton.requestFocus();
}
}
/**
* True iff the toolbar is responding to events. This may need to be turned off temporarily.
*/
private boolean isRespondingToEvents() {
return this.respondingToEvents;
}
/**
* Sets whether the toolbar should react to events. This may need to be turned off temporarily.
*/
public void setRespondingToEvents(boolean respondingToEvents) {
this.respondingToEvents = respondingToEvents;
}
protected void processKeyEvent(KeyEvent e) {
System.out.println("process key event " + e);
super.processKeyEvent(e);
}
/**
* Constructs the button with the given node type and image prefix. If the node type is "Select", constructs a
* button that allows nodes to be selected and moved. If the node type is "Edge", constructs a button that allows
* edges to be drawn. For other node types, constructs buttons that allow those type of nodes to be added to the
* workbench. If a non-null image prefix is provided, images for Up.gif, Down.gif,
* Off.gif and Roll.gif are loaded from the /images
* directory relative to this compiled class and used to provide up, down, off, and rollover images for the
* constructed button. On construction, nodes are mapped to their node types in the Map, nodeTypes
.
* Listeners are added to the node.
*
* @param buttonInfo contains the info needed to construct the button.
*/
private JToggleButton constructButton(ButtonInfo buttonInfo) {
String imagePrefix = buttonInfo.getImagePrefix();
if (imagePrefix == null) {
throw new NullPointerException("Image prefix must not be null.");
}
JToggleButton button = new JToggleButton();
button.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
setShiftDown(e.isShiftDown());
// setControlDown(e.isControlDown());
}
});
if ("Select".equals(buttonInfo.getNodeTypeName())) {
button.setIcon(new ImageIcon(ImageUtils.getImage(this, "move.gif")));
} else if ("Edge".equals(buttonInfo.getNodeTypeName())) {
button.setIcon(
new ImageIcon(ImageUtils.getImage(this, "flow.gif")));
} else {
button.setName(buttonInfo.getNodeTypeName());
button.setText("" + buttonInfo.getDisplayName()
+ " ");
}
button.setMaximumSize(new Dimension(110, 40)); // For a vertical box.
button.setToolTipText(buttonInfo.getToolTipText());
this.nodeTypes.put(button, buttonInfo.getNodeTypeName());
return button;
}
/**
* Sets the state of the workbench in response to a button press.
*
* @param button the JToggleButton whose workbench state is to be set.
*/
private void setWorkbenchMode(JToggleButton button) {
String nodeType = this.nodeTypes.get(button);
/*
The node type of the button that is used for the edge-drawing tool.
*/
final String edgeType = "Edge";
if (this.selectType.equals(nodeType)) {
this.workbench.setWorkbenchMode(AbstractWorkbench.SELECT_MOVE);
this.workbench.setNextButtonType(null);
setCursor(new Cursor(Cursor.HAND_CURSOR));
this.workbench.setCursor(new Cursor(Cursor.HAND_CURSOR));
} else if (edgeType.equals(nodeType)) {
this.workbench.setWorkbenchMode(AbstractWorkbench.ADD_EDGE);
this.workbench.setNextButtonType(null);
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
this.workbench.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
} else {
this.workbench.setWorkbenchMode(AbstractWorkbench.ADD_NODE);
this.workbench.setNextButtonType(nodeType);
setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
this.workbench.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
}
}
/**
* @return the JToggleButton for the given node type, or null if no such button exists.
*/
private JToggleButton getButtonForType(String nodeType) {
for (JToggleButton o : this.nodeTypes.keySet()) {
if (nodeType.equals(this.nodeTypes.get(o))) {
return o;
}
}
return null;
}
private boolean isShiftDown() {
return this.shiftDown;
}
private void setShiftDown(boolean shiftDown) {
this.shiftDown = shiftDown;
}
/**
* Holds info for constructing a single button.
*/
private static final class ButtonInfo {
/**
* This is the name used to construct nodes on the graph of this type. Need to coordinate with session.
*/
private final String nodeTypeName;
/**
* The name displayed on the button.
*/
private final String displayName;
/**
* The prefixes for images for this button. It is assumed that files
* Up.gif, Down.gif, Off.gif and
* Roll.gif are located in the /images directory relative to
* this compiled class.
*/
private final String imagePrefix;
/**
* Tool tip text displayed for the button.
*/
private final String toolTipText;
public ButtonInfo(String nodeTypeName, String displayName,
String imagePrefix, String toolTipText) {
this.nodeTypeName = nodeTypeName;
this.displayName = displayName;
this.imagePrefix = imagePrefix;
this.toolTipText = toolTipText;
}
public String getNodeTypeName() {
return this.nodeTypeName;
}
public String getDisplayName() {
return this.displayName;
}
public String getImagePrefix() {
return this.imagePrefix;
}
public String getToolTipText() {
return this.toolTipText;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy