FontStore in Java

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

Download ZIP

Back