package greenfoot.guifx.classes;
import bluej.Config;
import bluej.debugger.gentype.Reflective;
import bluej.editor.Editor;
import bluej.extensions.SourceType;
import bluej.pkgmgr.Package;
import bluej.pkgmgr.target.ClassTarget;
import bluej.pkgmgr.target.DependentTarget.State;
import bluej.pkgmgr.target.DependentTarget.TargetListener;
import bluej.utility.javafx.JavaFXUtil;
import greenfoot.guifx.GreenfootStage;
import greenfoot.guifx.classes.GClassDiagram.GClassType;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.image.Image;
import javafx.scene.input.ContextMenuEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import threadchecker.OnThread;
import threadchecker.Tag;
import java.io.File;
import java.lang.reflect.Modifier;
import java.util.List;
| A version of GClassNode that handles extra actions and updates for local classes
| (i.e. classes that are in this project, rather than Actor and World
| which are imported).
|
public class LocalGClassNode
extends GClassNode implements TargetListener{
private final GClassType type;
private GClassDiagram classDiagram;
private final ClassTarget classTarget;
private String imageFilename;
| Make an instance for the given ClassTarget. The image will be retrieved from the project
| properties.
|
| @param classTarget The ClassTarget to make an instance for
| @param subClasses The sub-classes of this GClassNode
| @param type The type of this class (Actor/World child, or Other)
|
public LocalGClassNode(GClassDiagram classDiagram, ClassTarget classTarget,
List<GClassNode> subClasses, GClassType type)
{
super(getImageForClass(classTarget, type), subClasses, classDiagram.getSelectionManager());
this.imageFilename = classTarget.getPackage().getLastSavedProperties()
.getProperty("class." + classTarget.getQualifiedName() + ".image");
this.classDiagram = classDiagram;
this.classTarget = classTarget;
this.type = type;
setImageForEditor();
}
@Override
public String getQualifiedName()
{
return classTarget.getQualifiedName();
}
@Override
public String getDisplayName()
{
return classTarget.getBaseName();
}
| Get the image for a class, if any. A class "inherits" its super-class image if it does not
|* have a specific image set. May return null.
* @param classTarget The ClassTarget to get image for.
* @param type The source type of this class node.
* @return The image for the class or null if it has no image.
private static Image getImageForClass(ClassTarget classTarget, GClassType type)
{
if (type == GClassType.OTHER)
{
return null;
}
return JavaFXUtil.loadImage(getImageFilename(classTarget));
}
| Returns a file name for the image of the first class in the given class' class hierarchy
| that has an image set.
|
private static File getImageFilename(ClassTarget ct)
{
String className = ct.getQualifiedName();
Reflective type = ct.getTypeReflective();
Package pkg = ct.getPackage();
do {
String imageFileName = pkg.getLastSavedProperties()
.getProperty("class." + className + ".image");
if (imageFileName != null)
{
File imageDir = new File(pkg.getProject().getProjectDir(), "images");
return new File(imageDir, imageFileName);
}
if (type != null)
{
type = type.getSuperTypesR().stream().filter(t -> !t.isInterface()).findFirst().orElse(null);
className = (type != null) ? type.getName() : null;
}
}
while (type != null){;
return null;
}
}
| Setup the given ClassDisplay with custom actions
|
@Override
protected void setupClassDisplay(GreenfootStage greenfootStage, ClassDisplay display)
{
display.setOnContextMenuRequested(e -> {
e.consume();
showContextMenu(greenfootStage, display, e);
});
display.setOnMouseClicked(e -> {
if (e.getButton() == MouseButton.PRIMARY && e.getClickCount() == 2)
{
classTarget.open();
}
});
classTarget.addListener(this);
stateChanged(classTarget.getState());
}
@Override
public void editorOpened()
{
setImageForEditor();
}
| Set the header image for the editor, if it has been opened.
|
private void setImageForEditor()
{
Editor editor = classTarget.getEditorIfOpen();
if (editor != null)
{
editor.setHeaderImage(image);
}
}
@Override
protected void setImage(Image newImage)
{
super.setImage(newImage);
setImageForEditor();
}
@Override
public void stateChanged(State newState)
{
Paint fill;
switch (newState)
{
case NEEDS_COMPILE:
fill = ClassTarget.getGreyStripeFill();
break;
case HAS_ERROR:
fill = ClassTarget.getRedStripeFill();
break;
default:
fill = Color.TRANSPARENT;
}
display.setStripePattern(fill);
if (newState != State.COMPILED)
{
classDiagram.getGreenfootStage().classModified();
}
}
@Override
public void renamed(String newName)
{
classDiagram.getGreenfootStage().saveAndMirrorClassImageFilename(newName, getImageFilename());
classDiagram.recalculateGroups();
}
@Override
public void tidyup()
{
super.tidyup();
classTarget.removeListener(this);
}
| Shows a context menu.
| @param greenfootStage The GreenfootStage, needed for some actions
| @param display The ClassDisplay we are showing the menu on.
| @param e The event that triggered the showing.
|
@OnThread(Tag.FXPlatform)
private void showContextMenu(GreenfootStage greenfootStage, ClassDisplay display, ContextMenuEvent e)
{
if (curContextMenu != null)
{
curContextMenu.hide();
curContextMenu = null;
classDiagram.hideContextMenu();
}
ContextMenu contextMenu = new ContextMenu();
Class<?> cl = null;
if (classTarget.isCompiled())
{
cl = classTarget.getPackage().loadClass(classTarget.getQualifiedName());
}
contextMenu.getScene().setOnMouseMoved(ev -> greenfootStage.setLatestMousePosOnScreen(ev.getScreenX(), ev.getScreenY()));
if (cl != null)
{
if (classTarget.getRole().createClassConstructorMenu(contextMenu.getItems(), classTarget, cl))
{
contextMenu.getItems().add(new SeparatorMenuItem());
}
if (classTarget.getRole().createClassStaticMenu(contextMenu.getItems(), classTarget, cl))
{
contextMenu.getItems().add(new SeparatorMenuItem());
}
}
else
{
MenuItem menuItem = new MenuItem(Config.getString("classPopup.needsCompile"));
menuItem.setDisable(true);
contextMenu.getItems().add(menuItem);
contextMenu.getItems().add(new SeparatorMenuItem());
}
if (classTarget.hasSourceCode() || classTarget.getDocumentationFile().exists())
{
contextMenu.getItems().add(GClassDiagram.contextInbuilt(
Config.getString(classTarget.hasSourceCode() ? "edit.class" : "show.apidoc"),
classTarget::open));
}
if (type == GClassType.ACTOR || type == GClassType.WORLD)
{
contextMenu.getItems().add(GClassDiagram.contextInbuilt(Config.getString("select.image"),
() -> greenfootStage.setImageFor(this)));
}
contextMenu.getItems().add(classTarget.new InspectAction(cl != null, display));
contextMenu.getItems().add(new SeparatorMenuItem());
if (classTarget.hasSourceCode())
{
contextMenu.getItems().add(GClassDiagram.contextInbuilt(Config.getString("duplicate.class"),
() -> greenfootStage.duplicateClass(this, classTarget)));
}
contextMenu.getItems().add(GClassDiagram.contextInbuilt(Config.getString("remove.class"), () -> {
classTarget.remove();
classDiagram.recalculateGroups();
greenfootStage.fireWorldRemovedCheck(classTarget);
}));
if (classTarget.getSourceType() == SourceType.Stride)
{
contextMenu.getItems().add(classTarget.new ConvertToJavaAction(greenfootStage));
}
else if (classTarget.getSourceType() == SourceType.Java &&
classTarget.getRole() != null && classTarget.getRole().canConvertToStride())
{
contextMenu.getItems().add(classTarget.new ConvertToStrideAction(greenfootStage));
}
boolean isFinal = false;
if (cl != null)
{
isFinal = Modifier.isFinal(cl.getModifiers());
}
else
{
Reflective ctReflective = classTarget.getTypeReflective();
if (ctReflective != null)
{
isFinal = ctReflective.isFinal();
}
}
if (! isFinal)
{
contextMenu.getItems().add(GClassDiagram.contextInbuilt(Config.getString("new.sub.class"),
() -> greenfootStage.newSubClassOf(classTarget.getQualifiedName(), type)));
}
classDiagram.getSelectionManager().select(display);
contextMenu.show(display, e.getScreenX(), e.getScreenY());
curContextMenu = contextMenu;
}
@Override
public String getImageFilename()
{
return imageFilename;
}
| Set the image filename for this class node. The displayed image will be changed to match.
|
public void setImageFilename(String newImageFilename)
{
this.imageFilename = newImageFilename;
if (newImageFilename != null)
{
File imageDir = new File(classTarget.getPackage().getProject().getProjectDir(), "images");
File imageFile = new File(imageDir, imageFilename);
setImage(JavaFXUtil.loadImage(imageFile));
}
else
{
setImage(null);
}
}
| Get the class target of this class
|
public ClassTarget getClassTarget()
{
return classTarget;
}
}
top,
use,
map,
class LocalGClassNode
. LocalGClassNode
. getQualifiedName
. getDisplayName
. getImageForClass
. getImageFilename
. setupClassDisplay
. editorOpened
. setImageForEditor
. setImage
. stateChanged
. renamed
. tidyup
. showContextMenu
. getImageFilename
. setImageFilename
. getClassTarget
426 neLoCode
+ 19 LoComm