package greenfoot.guifx.classes;
import bluej.Config;
import bluej.extensions.SourceType;
import bluej.utility.Debug;
import bluej.utility.javafx.JavaFXUtil;
import greenfoot.guifx.GreenfootStage;
import greenfoot.util.GreenfootUtil;
import javafx.geometry.Insets;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebView;
import javafx.stage.Modality;
import threadchecker.OnThread;
import threadchecker.Tag;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.FileFilter;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
| A dialog showing the possible importable classes in the "common" directory in the Greenfoot
|* installation. The user can select a class and see the documentation.
*/
@OnThread(Tag.FXPlatform)
public class ImportClassDialog extends Dialog<File>{
private final ClassDisplaySelectionManager classDisplaySelectionManager = new ClassDisplaySelectionManager();
|
|// Maps fully-qualified class names to originating files:
|
|private final Map<String, File> filesForQualifiedClasses = new HashMap<>();
|
|/**
| Create a dialog, with the given parent GreenfootStage
|
public ImportClassDialog(GreenfootStage greenfootStage)
{
initModality(Modality.APPLICATION_MODAL);
initOwner(greenfootStage);
setTitle(Config.getString("import.dialogTitle"));
ClassGroup classGroup = new ClassGroup(greenfootStage);
List<ImportableGClassNode> foundClasses = findImportableClasses(new File(Config.getGreenfootLibDir(), "common"));
Collections.sort(foundClasses, Comparator.comparing(c -> c.getDisplayName()));
classGroup.getLiveTopLevelClasses().addAll(foundClasses);
classGroup.updateAfterAdd();
for (ImportableGClassNode foundClass : foundClasses)
{
filesForQualifiedClasses.put(foundClass.getQualifiedName(), foundClass.file);
}
WebView docView = new WebView();
classDisplaySelectionManager.addSelectionListener(selection -> {
File file = selection == null ? null : filesForQualifiedClasses.get(selection.getQualifiedName());
docView.setVisible(false);
if (file != null)
{
File htmlFile = new File(GreenfootUtil.removeExtension(file.getAbsolutePath()) + ".html");
if (htmlFile.exists())
{
try
{
docView.getEngine().load(htmlFile.toURI().toURL().toExternalForm());
docView.setVisible(true);
}
catch (MalformedURLException e)
{
Debug.reportError(e);
}
}
}
});
if (!foundClasses.isEmpty())
{
classDisplaySelectionManager.select(foundClasses.get(0).getDisplay(greenfootStage));
}
getDialogPane().setContent(new BorderPane(docView, null, null, null, classGroup));
BorderPane.setMargin(classGroup, new Insets(0, 8, 0, 0));
getDialogPane().getButtonTypes().setAll(ButtonType.OK, ButtonType.CANCEL);
JavaFXUtil.addStyleClass(getDialogPane(), "import-class-dialog");
setResizable(true);
setWidth(700);
setHeight(550);
Config.addGreenfootStylesheets(getDialogPane().getScene());
setResultConverter(bt -> {
if (bt == ButtonType.OK)
{
ClassDisplay selected = classDisplaySelectionManager.getSelected();
if (selected != null)
{
return filesForQualifiedClasses.get(selected.getQualifiedName());
}
}
return null;
});
}
| Find all the importable classes in the given directory and all its subdirectories.
| @param dir The directory to search (must be non null)
| @return The list of all classes found.
|
private List findImportableClasses(File dir)
{
List<ImportableGClassNode> foundClasses = new ArrayList<>();
File[] files = dir.listFiles(new ImportableClassesFileFilter());
if (files != null)
{
for (File file : files)
{
foundClasses.add(new ImportableGClassNode(file));
}
}
File[] folders = dir.listFiles(new ImportableFoldersFileFilter());
if (folders != null)
{
for (File folder : folders)
{
foundClasses.addAll(findImportableClasses(folder));
}
}
return foundClasses;
}
| Filter to find importable items (Java, Stride, Class)
|
private static class ImportableClassesFileFilter
implements FileFilter
{
@Override
@OnThread(Tag.Any)
public boolean accept(File pathname)
{
return pathname.getAbsolutePath().endsWith(".class")
|| pathname.getAbsolutePath().endsWith("." + SourceType.Java.toString().toLowerCase())
|| pathname.getAbsolutePath().endsWith("." + SourceType.Stride.toString().toLowerCase());
}
}
| Filter to find folders
|
private static class ImportableFoldersFileFilter
implements FileFilter
{
@Override
@OnThread(Tag.Any)
public boolean accept(File pathname)
{
return pathname.isDirectory();
}
}
| Looks for an image that might be associated with the given class.
|
| So given /foo/Crab.java, /foo/Crab.stride or /foo/Crab.class,
| it looks (case insensitive) for /foo/crab.png, /foo/Crab.jpg, etc
|
| @param classFile The original file, the extension of which will be ignored.
| @return The image file found (arbitrary pick if multiple), or null if none found.
|
public static File findImage(File classFile)
{
String[] extensions = ImageIO.getReaderFileSuffixes();
File directory = classFile.getAbsoluteFile().getParentFile();
String stemName = GreenfootUtil.removeExtension(classFile.getAbsoluteFile().getName());
File[] allFiles = directory.listFiles();
if (allFiles == null)
{
return null;
}
for (File f : allFiles)
{
for (String ext : extensions)
{
if (f.getName().equalsIgnoreCase(stemName + "." + ext))
{
return f;
}
}
}
return null;
}
| A GClassNode used for display. Overrides parent to remove any context menus, and
| stores the file associated with the class.
|
@OnThread(Tag.FXPlatform)
private class ImportableGClassNode
extends GClassNode
{
private final File file;
public ImportableGClassNode(File file)
{
super(
JavaFXUtil.loadImage(findImage(file)),
Collections.emptyList(),
classDisplaySelectionManager);
this.file = file;
}
@Override
public String getQualifiedName()
{
return GreenfootUtil.removeExtension(file.getName());
}
@Override
public String getDisplayName()
{
return getQualifiedName();
}
@Override
protected void setupClassDisplay(GreenfootStage greenfootStage, ClassDisplay display)
{
display.setOnMouseClicked(e -> {
if (e.getButton() == MouseButton.PRIMARY && e.getClickCount() == 2)
{
setResult(file);
close();
}
});
}
}
}
. ImportClassDialog
. findImportableClasses
top,
use,
map,
class ImportableClassesFileFilter
. accept
top,
use,
map,
class ImportableFoldersFileFilter
. accept
. findImage
top,
use,
map,
class ImportableGClassNode
. ImportableGClassNode
. getQualifiedName
. getDisplayName
. setupClassDisplay
264 neLoCode
+ 17 LoComm