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(); } }); } } }