package bluej.debugmgr.inspector;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.Window;
import bluej.Config;
import bluej.debugger.DebuggerField;
import bluej.debugger.DebuggerObject;
import bluej.pkgmgr.Package;
import bluej.pkgmgr.PackageEditor;
import bluej.pkgmgr.PkgMgrFrame;
import bluej.testmgr.record.GetInvokerRecord;
import bluej.testmgr.record.InvokerRecord;
import bluej.testmgr.record.ObjectInspectInvokerRecord;
import bluej.utility.DialogManager;
import bluej.utility.javafx.JavaFXUtil;
import javafx.stage.WindowEvent;
import javafx.util.Duration;
import threadchecker.OnThread;
import threadchecker.Tag;
|
| A window that displays the fields in an object or class. This class is
| subclassed for objects, classes and method results separately
| (ObjectInspector, ClassInspector, ResultInspector).
|
| @author Michael Kolling
| @author Poul Henriksen
| @author Bruce Quig
|
@OnThread(Tag.FXPlatform)
public abstract class Inspector
extends Stage{
protected final static String showClassLabel = Config.getString("debugger.inspector.showClass");
protected final static String inspectLabel = Config.getString("debugger.inspector.inspect");
protected final static String getLabel = Config.getString("debugger.inspector.get");
protected final static String close = Config.getString("close");
protected FieldList fieldList = null;
protected Button inspectButton;
protected Button getButton;
protected AssertPanel assertPanel;
protected DebuggerObject selectedField;
protected String selectedFieldName;
protected String selectedFieldType;
protected InvokerRecord selectedInvokerRecord;
protected final Package pkg;
protected final InspectorManager inspectorManager;
protected final InvokerRecord ir;
private double initialClickX;
private double initialClickY;
private static AtomicInteger nextUniqueId = new AtomicInteger(1);
private final int uniqueId;
private static final int MIN_LIST_WIDTH = 150;
private static final int MAX_LIST_WIDTH = 400;
private final ResizeListener resizeListener;
| Convert a field to a string representation, used to display the field in the inspector value list.
|
@OnThread(Tag.FXPlatform)
public static String fieldToString(DebuggerField field)
{
int mods = field.getModifiers();
String result = "";
if (Modifier.isPrivate(mods)) {
result = "private ";
}
else if (Modifier.isPublic(mods)) {
result = "public ";
}
else if (Modifier.isProtected(mods)) {
result = "protected ";
}
if (field.isHidden()) {
result += "(hidden) ";
}
result += field.getType().toString(true);
result += " " + field.getName();
return result;
}
| Constructor.
|
| @param pkg
| the package this inspector belongs to (or null)
| @param ir
| the InvokerRecord for this inspector (or null)
|
protected Inspector(InspectorManager inspectorManager, Package pkg, InvokerRecord ir, StageStyle stageStyle)
{
if (inspectorManager == null) {
throw new NullPointerException("An inspector must have an InspectorManager.");
}
if (pkg == null && ir != null) {
ir = null;
}
JavaFXUtil.addSelfRemovingListener(sceneProperty(), Config::addInspectorStylesheets);
initStyle(stageStyle);
this.inspectorManager = inspectorManager;
this.pkg = pkg;
this.ir = ir;
this.uniqueId = nextUniqueId.incrementAndGet();
setOnCloseRequest(e -> { e.consume(); doClose(true);
});
addEventFilter(javafx.scene.input.KeyEvent.KEY_PRESSED, e -> {
if (e.getCode() == KeyCode.ESCAPE)
doClose(true);
});
resizeListener = new ResizeListener(this);
final Timeline autoUpdate = new Timeline(new KeyFrame(Duration.seconds(1), e -> {
update();
}));
autoUpdate.setCycleCount(Timeline.INDEFINITE);
addEventHandler(WindowEvent.ANY, e -> {
boolean shown = e.getEventType() == WindowEvent.WINDOW_SHOWN;
boolean hidden = e.getEventType() == WindowEvent.WINDOW_HIDDEN;
if (hidden)
{
autoUpdate.stop();
}
else if (shown && shouldAutoUpdate())
{
autoUpdate.playFromStart();
}
});
initFieldList();
}
| Should we auto-update the inspector window every second while it is showing?
| Currently true for class and object inspectors in Greenfoot only.
|
protected abstract boolean shouldAutoUpdate();
| Initializes the list of fields. This creates the component that shows the
| fields.
| @param valueFieldColor
|
private void initFieldList()
{
fieldList = new FieldList();
JavaFXUtil.addChangeListenerPlatform(fieldList.getSelectionModel().selectedIndexProperty(), index -> listElementSelected(index.intValue()));
fieldList.setOnMouseClicked(e -> {
if (e.getClickCount() == 2 && e.getButton() == MouseButton.PRIMARY)
{
doInspect();
}
});
fieldList.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
if (e.getCode() == KeyCode.ENTER) {
doClose(true);
e.consume();
}
});
}
protected boolean isGetEnabled()
{
return ir != null;
}
| De-iconify the window (if necessary) and bring it to the front.
|
public void bringToFront()
{
setIconified(false);
toFront();
}
| Returns the list of data.
|
abstract protected List getListData();
| An element in the field list was selected.
|
abstract protected void listElementSelected(int slot);
| Remove this inspector.
|
abstract protected void remove();
| Return the preferred number of rows that should be shown in the list
|
| @return The number of rows
|
abstract protected int getPreferredRows();
| Requests an update of the field values shown in this viewer to show current object
| values.
|
public void update()
{
final List<FieldInfo> listData = getListData();
int prevSelection = fieldList.getSelectionModel().getSelectedIndex();
fieldList.setData(listData);
if (!listData.isEmpty())
fieldList.getSelectionModel().select(prevSelection == -1 || prevSelection >= listData.size() ? 0 : prevSelection);
}
| Store the object currently selected in the list.
|
| @param object
| The new CurrentObj value
| @param name
| The name of the selected field
| @param type
| The type of the selected field
|
protected void setCurrentObj(DebuggerObject object, String name, String type)
{
selectedField = object;
selectedFieldName = name;
selectedFieldType = type;
}
| Enable or disable the Inspect and Get buttons.
|
| @param inspect
| The new ButtonsEnabled value
| @param get
| The new ButtonsEnabled value
|
protected void setButtonsEnabled(boolean inspect, boolean get)
{
inspectButton.setDisable(!inspect);
getButton.setDisable(!(get && isGetEnabled()));
}
| The "Inspect" button was pressed. Inspect the selected object.
|*/
protected void doInspect()
{
if (selectedField != null) {
boolean isPublic = !getButton.isDisable();
InvokerRecord newIr = new ObjectInspectInvokerRecord(selectedFieldName, ir);
|
|inspectorManager.getInspectorInstance(selectedField, selectedFieldName, pkg, isPublic ? newIr : null, this, null);
|
|}
|
|}
|
|/**
| The "Get" button was pressed. Get the selected object on the object
|* bench.
*/
protected void doGet()
{
if (selectedField != null) {
GetInvokerRecord getIr = new GetInvokerRecord(selectedFieldType, selectedFieldName, ir);
|
|DebuggerObject selField = this.selectedField;
|
|PackageEditor pkgEd = pkg.getEditor();
|
|pkgEd.recordInteraction(getIr);
|
|pkgEd.raisePutOnBenchEvent(this, selField, selField.getGenType(), getIr, true, Optional.empty());
|
|}
|
|}
|
|/**
| Close this inspector. The caller should remove it from the list of open
| inspectors.
|
| @param handleAssertions Whether assertions should be attached to the
| invoker record. If true, the user may be prompted
| to fill in assertion data.
|
public void doClose(boolean handleAssertions)
{
boolean closeOk = true;
if (handleAssertions) {
closeOk = handleAssertions();
}
if (closeOk) {
hide();
remove();
}
}
protected boolean handleAssertions()
{
if (assertPanel != null && assertPanel.isAssertEnabled()) {
if (! assertPanel.isAssertComplete()) {
int choice = DialogManager.askQuestionFX(this, "empty-assertion-text");
if (choice == 0) {
return false;
}
}
ir.addAssertion(assertPanel.getAssertStatement());
assertPanel.recordAssertion(pkg, () -> Optional.ofNullable(PkgMgrFrame.findFrame(pkg)).map(PkgMgrFrame::getTestIdentifier), ir.getUniqueIdentifier());
}
return true;
}
protected Button createCloseButton()
{
Button button = new Button(close);
button.setOnAction(e -> doClose(true));
return button;
}
| Creates a panel with an inspect button and a get button
|
| @return A panel with two buttons
|
protected Node createInspectAndGetButtons()
{
Pane buttonPanel = new VBox();
inspectButton = new Button(inspectLabel);
inspectButton.setOnAction(e -> doInspect());
inspectButton.setDisable(true);
buttonPanel.getChildren().add(inspectButton);
getButton = new Button(getLabel);
getButton.setVisible(!Config.isGreenfoot());
getButton.setDisable(true);
getButton.setOnAction(e -> doGet());
buttonPanel.getChildren().add(getButton);
JavaFXUtil.addStyleClass(buttonPanel, "inspector-side-buttons");
return buttonPanel;
}
protected void installListenersForMoveDrag(double curvedCornersMargin)
{
resizeListener.setCurvedCorners(curvedCornersMargin);
addEventHandler(MouseEvent.MOUSE_MOVED, resizeListener);
addEventHandler(MouseEvent.MOUSE_PRESSED, resizeListener);
addEventFilter(MouseEvent.MOUSE_DRAGGED, resizeListener);
addEventHandler(MouseEvent.MOUSE_EXITED, resizeListener);
addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, resizeListener);
addEventHandler(MouseEvent.MOUSE_PRESSED, e ->
{
initialClickX = e.getScreenX() - getX();
initialClickY = e.getScreenY() - getY();
});
addEventHandler(MouseEvent.MOUSE_DRAGGED, e ->
{
setX(e.getScreenX() - initialClickX);
setY(e.getScreenY() - initialClickY);
});
}
@OnThread(Tag.Any)
public int getUniqueId()
{
return uniqueId;
}
public void centerOnOwner()
{
getScene().getRoot().applyCss();
getScene().getRoot().layout();
final Window owner = getOwner();
if (owner == null)
{
centerOnScreen();
return;
}
final Scene ownerScene = owner.getScene();
final double titleBarHeight = ownerScene.getY();
final double dialogWidth = getScene().getRoot().prefWidth(-1);
final double dialogHeight = getScene().getRoot().prefHeight(dialogWidth);
double x = owner.getX() + (ownerScene.getWidth() / 2.0) - (dialogWidth / 2.0);
double y = owner.getY() + titleBarHeight / 2.0 + (ownerScene.getHeight() / 2.0) - (dialogHeight / 2.0);
setX(x);
setY(y);
}
public abstract Region getContent();
| Adapted from:
| http://stackoverflow.com/questions/19455059/allow-user-to-resize-an-undecorated-stage
|
static class ResizeListener implements EventHandler<MouseEvent>
{
private Stage stage;
private Cursor cursorEvent = Cursor.DEFAULT;
private int border = 4;
private double startX = 0;
private double startY = 0;
private double curvedCornersMargin;
public ResizeListener(Stage stage)
{
this.stage = stage;
}
@Override
public void handle(MouseEvent mouseEvent)
{
EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
Scene scene = stage.getScene();
double mouseEventX = mouseEvent.getSceneX(),
mouseEventY = mouseEvent.getSceneY(),
sceneWidth = scene.getWidth(),
sceneHeight = scene.getHeight();
if (MouseEvent.MOUSE_MOVED.equals(mouseEventType) == true) {
double cornerBorder = Math.max(curvedCornersMargin, border);
if (mouseEventX < cornerBorder && mouseEventY < cornerBorder) {
cursorEvent = Cursor.NW_RESIZE;
}
else if (mouseEventX < cornerBorder && mouseEventY > sceneHeight - cornerBorder) {
cursorEvent = Cursor.SW_RESIZE;
}
else if (mouseEventX > sceneWidth - cornerBorder && mouseEventY < cornerBorder) {
cursorEvent = Cursor.NE_RESIZE;
}
else if (mouseEventX > sceneWidth - cornerBorder && mouseEventY > sceneHeight - cornerBorder) {
cursorEvent = Cursor.SE_RESIZE;
}
else if (mouseEventX < border) {
cursorEvent = Cursor.W_RESIZE;
}
else if (mouseEventX > sceneWidth - border) {
cursorEvent = Cursor.E_RESIZE;
}
else if (mouseEventY < border) {
cursorEvent = Cursor.N_RESIZE;
}
else if (mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.S_RESIZE;
}
else {
cursorEvent = Cursor.DEFAULT;
}
scene.setCursor(cursorEvent);
}
else if (MouseEvent.MOUSE_EXITED.equals(mouseEventType) || MouseEvent.MOUSE_EXITED_TARGET.equals(mouseEventType)){
scene.setCursor(Cursor.DEFAULT);
}
else if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType) == true) {
startX = stage.getWidth() - mouseEventX;
startY = stage.getHeight() - mouseEventY;
}
else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType) == true) {
if (Cursor.DEFAULT.equals(cursorEvent) == false) {
if (Cursor.W_RESIZE.equals(cursorEvent) == false && Cursor.E_RESIZE.equals(cursorEvent) == false) {
double minHeight = stage.getMinHeight() > (border*2) ? stage.getMinHeight() : (border*2);
if (Cursor.NW_RESIZE.equals(cursorEvent) == true || Cursor.N_RESIZE.equals(cursorEvent) == true || Cursor.NE_RESIZE.equals(cursorEvent) == true) {
if (stage.getHeight() > minHeight || mouseEventY < 0) {
stage.setHeight(stage.getY() - mouseEvent.getScreenY() + stage.getHeight());
stage.setY(mouseEvent.getScreenY());
}
}
else {
if (stage.getHeight() > minHeight || mouseEventY + startY - stage.getHeight() > 0) {
stage.setHeight(mouseEventY + startY);
}
}
}
if (Cursor.N_RESIZE.equals(cursorEvent) == false && Cursor.S_RESIZE.equals(cursorEvent) == false) {
double minWidth = stage.getMinWidth() > (border*2) ? stage.getMinWidth() : (border*2);
if (Cursor.NW_RESIZE.equals(cursorEvent) == true || Cursor.W_RESIZE.equals(cursorEvent) == true || Cursor.SW_RESIZE.equals(cursorEvent) == true) {
if (stage.getWidth() > minWidth || mouseEventX < 0) {
stage.setWidth(stage.getX() - mouseEvent.getScreenX() + stage.getWidth());
stage.setX(mouseEvent.getScreenX());
}
}
else {
if (stage.getWidth() > minWidth || mouseEventX + startX - stage.getWidth() > 0) {
stage.setWidth(mouseEventX + startX);
}
}
}
mouseEvent.consume();
}
}
}
public void setCurvedCorners(double curvedCornersMargin)
{
this.curvedCornersMargin = curvedCornersMargin;
}
}
}
top,
use,
map,
abstract class Inspector
. fieldToString
. Inspector
. shouldAutoUpdate
. initFieldList
. isGetEnabled
. bringToFront
. getListData
. listElementSelected
. remove
. getPreferredRows
. update
. setCurrentObj
. setButtonsEnabled
. doClose
. handleAssertions
. createCloseButton
. createInspectAndGetButtons
. installListenersForMoveDrag
. getUniqueId
. centerOnOwner
. getContent
. ResizeListener
. handle
. setCurvedCorners
588 neLoCode
+ 59 LoComm