FontStore in Java
--------------- FontStore.java -----------------
import java.io.*;
import java.util.Map.*;
import java.util.AbstractMap.*;
import java.nio.file.*;
import java.util.*;
import java.text.*;
import javafx.application.*;
import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.text.*;
import javafx.scene.input.*;
import javafx.geometry.*;
import javafx.collections.*;
import javafx.collections.transformation.*;
import javafx.beans.property.*;
import javafx.event.*;
public class FontStore extends Application {
final String pangram = "The quick brown fox jumps over the lazy dog";
//final String pangram = "Jackdaws love my big sphinx of quartz";
//final String pangram = "The five boxing wizards jump quickly";
String pattern = pangram;
double fontSize = 24;
FileSystem fs = null;
ObservableList<Entry<Path, Entry<Font, ?>>> data = null;
Path currentPath;
TableView<Entry<Path, Entry<Font, ?>>> tableView;
TableRow<Entry<Path, Entry<Font, ?>>> currentRow = null;
ContextMenu contextMenu = new ContextMenu();
Stage stage;
public void start(Stage stage) {
this.stage = stage;
BorderPane layout = new BorderPane();
stage.setScene(new Scene(layout, 700, 400));
open("archive.zip");
fontTable();
MenuBar menuBar = new MenuBar();
Menu miFile = new Menu("File");
MenuItem miFileExit = new MenuItem("Exit");
miFileExit.setOnAction(ae -> { close(); Platform.exit(); System.exit(0); });
miFile.getItems().addAll(miFileExit);
menuBar.getMenus().addAll(miFile);
layout.setTop(menuBar);
layout.setCenter(tableView);
layout.setLeft(fontTree());
stage.setOnCloseRequest(ev -> { close(); });
stage.show();
}
boolean open(String name) {
Path p = Paths.get(name);
try {
if (!Files.exists(p)) {
java.util.zip.ZipOutputStream zos = new java.util.zip.ZipOutputStream(Files.newOutputStream(p));
zos.close();
}
fs = FileSystems.newFileSystem(p, null);
return true;
} catch (Exception e) { }
return false;
}
void close() { try { fs.close(); } catch (Exception e) { } }
void fontTable() {
tableView = new TableView<>();
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
tableView.setPlaceholder(new Label(null));
hideColumnHeader(tableView);
data = FXCollections.observableArrayList();
SortedList<Entry<Path, Entry<Font, ?>>> sortedList = new SortedList<>(data);
sortedList.comparatorProperty().bind(new SimpleObjectProperty<Comparator<Entry<Path, Entry<Font, ?>>>>((t1, t2) -> t1.getKey().compareTo(t2.getKey())));
tableView.setItems(sortedList);
Font fontBold = Font.font(Font.getDefault().getFamily(), FontWeight.BOLD, Font.getDefault().getSize());
TableColumn<Entry<Path, Entry<Font, ?>>, Entry<Path, Entry<Font, ?>>> column = new TableColumn<>();
column.setCellValueFactory(c -> new ReadOnlyObjectWrapper<Entry<Path, Entry<Font, ?>>>(c.getValue()));
column.setCellFactory(c -> new TableCell<Entry<Path, Entry<Font, ?>>, Entry<Path, Entry<Font, ?>>>() {
@Override
public void updateItem(Entry<Path, Entry<Font, ?>> item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
if (item.getValue() == null) {
Font font = null;
try {
font = Font.loadFont(Files.newInputStream(item.getKey()), fontSize);
} catch (Exception e) { }
item.setValue(new SimpleEntry<>(font, null));
}
Font font = item.getValue().getKey();
if (font == null) {
setGraphic(new Label("Error loading font: ".concat(item.getKey().toString())));
} else {
Label l1 = new Label(font.getName()); l1.setMinWidth(130); l1.setFont(fontBold);
Label l2 = new Label(" ".concat(item.getKey().toString()));
Label l3 = new Label(pattern); l3.setFont(font);
GridPane gridpane = new GridPane();
gridpane.add(l1, 1, 1);
gridpane.add(l2, 2, 1);
gridpane.add(l3, 1, 2, 2, 1);
setGraphic(gridpane);
}
}
}
});
tableView.getColumns().add(column);
MenuItem miView = new MenuItem("View");
miView.setOnAction(ae -> { view(); });
MenuItem miIns = new MenuItem("Insert");
miIns.setOnAction(ae -> { insert(); });
MenuItem miExtr = new MenuItem("Extract");
miExtr.setOnAction(ae -> { extract(); });
MenuItem miDel = new MenuItem("Delete");
miDel.setOnAction(ae -> { delete(); });
contextMenu.getItems().setAll(miView, new SeparatorMenuItem(), miIns, miExtr, new SeparatorMenuItem(), miDel);
tableView.setRowFactory(tv -> {
TableRow<Entry<Path, Entry<Font, ?>>> row = new TableRow<>();
row.setOnMouseClicked(me -> { currentRow = row; });
return row;
});
tableView.setOnMouseClicked(me -> {
if (contextMenu.isShowing()) contextMenu.hide();
if (currentPath != null) {
boolean b = (currentRow == null || currentRow.isEmpty());
if (me.getButton() == MouseButton.PRIMARY && me.getClickCount() == 2 && !b) {
view();
} else if (me.getButton() == MouseButton.SECONDARY) {
miView.setDisable(b);
miExtr.setDisable(b);
miDel.setDisable(b);
contextMenu.show((Node)me.getSource(), me.getScreenX(), me.getScreenY());
}
}
});
tableView.setOnKeyPressed(ke -> {
if (currentPath != null) {
if (ke.getCode() == KeyCode.INSERT) insert();
else if (ke.getCode() == KeyCode.DELETE) delete();
else if (ke.getCode() == KeyCode.ENTER) view();
else if (ke.getCode() == KeyCode.F5) extract();
}
});
}
Node fontTree() {
TreeTableView<Path> treeTableView = new TreeTableView<>();
treeTableView.setMaxWidth(150);
treeTableView.setRoot(iniTree());
treeTableView.setColumnResizePolicy(TreeTableView.CONSTRAINED_RESIZE_POLICY);
treeTableView.setPlaceholder(new Label(null));
hideColumnHeader(treeTableView);
TreeTableColumn<Path, String> column = new TreeTableColumn<>();
column.setCellValueFactory(c -> {
String s;
if (c.getValue().getValue() != null && c.getValue().getValue().getFileName() != null) {
s = c.getValue().getValue().getFileName().toString();
int l = s.length() - 1;
if (l >= 0 && s.charAt(l) == '/') s = s.substring(0, l);
} else s = "/";
return new ReadOnlyObjectWrapper<String>(s);
});
treeTableView.getColumns().add(column);
treeTableView.getSelectionModel().selectedItemProperty().addListener((o, v, n) -> {
if (n != null) {
fillTable(n.getValue());
tableView.getSelectionModel().selectFirst();
}
});
treeTableView.setOnMouseClicked(me -> {
if (me.getButton() == MouseButton.PRIMARY && me.getClickCount() == 2) {
tableView.requestFocus();
}
});
treeTableView.getSelectionModel().selectFirst();
return treeTableView;
}
void fillTable(Path directory) {
data.clear();
currentPath = directory;
if (directory != null) {
try {
DirectoryStream<Path> stream = Files.newDirectoryStream(directory, p -> !Files.isDirectory(p));
stream.forEach(p -> data.add(new SimpleEntry<>(p, null)));
} catch (Exception e) { e.printStackTrace(); }
}
}
TreeItem<Path> iniTree() {
TreeItem<Path> root = null;
try {
List<Path> listPath = new ArrayList<>();
fs.getRootDirectories().forEach(listPath::add);
if (listPath.size() == 1) {
root = getTreeItem(listPath.get(0));
} else {
root = new TreeItem<>();
for(Path p : listPath) {
root.getChildren().add(getTreeItem(p));
}
}
root.setExpanded(true);
} catch (Exception e) { e.printStackTrace(); }
return root;
}
TreeItem<Path> getTreeItem(Path directory) throws Exception {
TreeItem<Path> root = new TreeItem<Path>(directory);
DirectoryStream<Path> stream = Files.newDirectoryStream(directory, p -> Files.isDirectory(p));
for (Path f : stream) root.getChildren().add(getTreeItem(f));
root.setExpanded(true);
root.getChildren().sort((t1, t2) -> t1.getValue().compareTo(t2.getValue()));
return root;
}
void hideColumnHeader(Region table) {
table.widthProperty().addListener((v, o, n) -> {
Pane header = (Pane)table.lookup("TableHeaderRow");
if (header != null && header.isVisible()) {
header.setMaxHeight(0); header.setMinHeight(0); header.setPrefHeight(0);
header.setVisible(false); header.setManaged(false);
}
});
}
void insert() {
if (currentPath != null) {
FileChooser.ExtensionFilter fontFl = new FileChooser.ExtensionFilter("Font files (*.ttf; *.otf)", "*.ttf", "*.otf");
FileChooser.ExtensionFilter allFl = new FileChooser.ExtensionFilter("All files (*.*)", "*.*");
FileChooser fileChooser = new FileChooser();
fileChooser.getExtensionFilters().addAll(fontFl, allFl);
List<File> files = fileChooser.showOpenMultipleDialog(stage);
if (files != null) {
List<Entry<Path, Path>> list = new ArrayList<>();
List<Entry<Path, Entry<Font, ?>>> items = new ArrayList<>();
files.forEach(p -> {
list.add(new SimpleEntry<>(p.toPath(), currentPath.resolve(p.getName())));
});
List<Entry<Path, Path>> lr = flCopy(list);
lr.forEach(p -> {
Entry<Path, Entry<Font, ?>> item = null;
for (Entry<Path, Entry<Font, ?>> d : data) {
if (d.getKey().equals(p.getValue())) { item = d; break; }
}
if (item == null) {
item = new SimpleEntry<>(p.getValue(), null);
data.add(item);
}
items.add(item);
});
if (!items.isEmpty()) {
tableView.getSelectionModel().clearSelection();
items.forEach(p -> tableView.getSelectionModel().select(p));
tableView.getSelectionModel().getSelectedIndex();
tableView.scrollTo(items.get(0));
}
}
}
}
void delete() {
ObservableList<Entry<Path, Entry<Font, ?>>> items = FXCollections.observableArrayList(tableView.getSelectionModel().getSelectedItems());
if (!items.isEmpty() && alert(Alert.AlertType.CONFIRMATION, "Delete " + (items.size() == 1 ? "'" + items.get(0).getKey() + "'" : items.size() + " files") + "?", ButtonType.YES, ButtonType.NO) == ButtonType.YES) {
for(Entry<Path, Entry<Font, ?>> entry : items) {
try {
Files.deleteIfExists(entry.getKey());
data.remove(entry);
} catch (Exception e) { e.printStackTrace(); }
}
}
}
void extract() {
ObservableList<Entry<Path, Entry<Font, ?>>> items = tableView.getSelectionModel().getSelectedItems();
if (!items.isEmpty()) {
DirectoryChooser directoryChooser = new DirectoryChooser();
File initialDirectory = new File("c:\\"); //File.listRoots()[0]
if (initialDirectory.isDirectory()) directoryChooser.setInitialDirectory(initialDirectory);
File directory = directoryChooser.showDialog(stage);
if (directory != null) {
List<Entry<Path, Path>> list = new ArrayList<>();
items.forEach(p -> {
Path path = Paths.get(directory.toString(), p.getKey().getFileName().toString());
list.add(new SimpleEntry<>(p.getKey(), path));
});
flCopy(list);
}
}
}
List<Entry<Path, Path>> flCopy(List<Entry<Path, Path>> list) {
List<Entry<Path, Path>> ret = new ArrayList<>();
boolean tl = false;
for(Entry<Path, Path> entry : list) {
try {
boolean cp = true;
if (!tl && Files.exists(entry.getValue())) {
ButtonType yesToAll = new ButtonType("Yes to All", ButtonBar.ButtonData.YES);
ButtonType[] m = list.size() > 1 ?
new ButtonType[] { ButtonType.YES, yesToAll, ButtonType.NO, ButtonType.CANCEL } :
new ButtonType[] { ButtonType.YES, ButtonType.NO, ButtonType.CANCEL };
SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
GridPane gridPane = new GridPane();
gridPane.add(new Label("File '" + entry.getValue() +"' already exists. Overwrite?"), 1, 1, 3, 1);
gridPane.add(new Label("New:"), 1, 2);
gridPane.add(new Label("Existing:"), 1, 3);
Label l1 = new Label(Files.size(entry.getKey()) + " bytes, ");
Label l2 = new Label(Files.size(entry.getValue()) + " bytes, ");
gridPane.add(l1, 2, 2); GridPane.setHalignment(l1, HPos.RIGHT);
gridPane.add(l2, 2, 3); GridPane.setHalignment(l2, HPos.RIGHT);
gridPane.add(new Label(df.format(Files.getLastModifiedTime(entry.getKey()).toMillis())), 3, 2);
gridPane.add(new Label(df.format(Files.getLastModifiedTime(entry.getValue()).toMillis())), 3, 3);
ButtonType result = alert(Alert.AlertType.CONFIRMATION, gridPane, m);
if (result == ButtonType.NO) cp = false;
else if (result == yesToAll) tl = true;
else if (result != ButtonType.YES) break;
}
if (tl || cp) {
Files.copy(entry.getKey(), entry.getValue(), StandardCopyOption.REPLACE_EXISTING);
//Files.setLastModifiedTime(entry.getValue(), Files.getLastModifiedTime(entry.getKey()));
ret.add(entry);
}
} catch (Exception e) { e.printStackTrace(); }
}
return ret;
}
ButtonType alert(Alert.AlertType type, Object cont, ButtonType... bt) {
Alert alert = new Alert(type, null, bt);
alert.initOwner(stage);
alert.initStyle(StageStyle.UTILITY);
alert.setHeaderText(null);
if (cont instanceof String) alert.setContentText((String)cont);
else if (cont instanceof Node) alert.getDialogPane().setContent((Node)cont);
dpBehavior(alert.getDialogPane());
Optional<ButtonType> result = alert.showAndWait();
return (result.isPresent() ? result.get() : null);
}
void view() {
ObservableList<Entry<Path, Entry<Font, ?>>> items = tableView.getSelectionModel().getSelectedItems();
if (!items.isEmpty()) {
Entry<Path, Entry<Font, ?>> entry = items.get(0);
Font font = entry.getValue().getKey();
if (font != null) {
Dialog<ButtonType> dialog = new Dialog<>();
dialog.initOwner(stage);
dialog.initStyle(StageStyle.UTILITY);
dialog.setHeaderText(null);
long size = 0;
try { size = Files.size(entry.getKey()); } catch (Exception e) { }
String s = "Font: " + font.getName() + ", File name: " + entry.getKey().toString() + ", Size: " + size + " bytes.";
GridPane gridPane = new GridPane();
byte[] bArr = new byte[1];
for (int c = 32; c < 256; c++) {
bArr[0] = (byte)c;
Text text = new Text(new String(bArr));
text.setFont(font);
gridPane.add(text, c % 32, c / 32);
}
dialog.getDialogPane().setContent(new VBox(10, new Label(s), gridPane));
dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK);
dpBehavior(dialog.getDialogPane());
dialog.showAndWait();
}
}
}
void dpBehavior(DialogPane dialogPane) {
for (ButtonType bt : dialogPane.getButtonTypes()) {
Button b = (Button)dialogPane.lookupButton(bt);
b.setDefaultButton(false);
b.addEventHandler(KeyEvent.KEY_PRESSED, ke -> {
if (ke.getCode() == KeyCode.ENTER) {
((Button)ke.getTarget()).fire();
ke.consume();
}
});
}
}
}