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