2021년 11월 21일 일요일

Java FX Comics Viewer

1. Java FX Comics Viewer

package com.home.app;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Optional;

import com.home.fx.FX;
import com.home.fx.FxImageView;
import com.home.fx.FxRunnable;
import com.home.java.UnZipApi;

import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;

import javax.imageio.ImageIO;

public class FxComicsViewer extends Application {

	final String title = "ComicsViewer";
	int fontSize = 12;
	FxImageView imageView;
	UnZipApi images;
	Stage primaryStage;
	ComboBox<String> pages;
	Label status;
	int imagesIndex;
	boolean ctrl_pressed = false;

	public static void main(String[] args) {
		launch(args);
	}

	@Override
	public void start(Stage stage) throws IOException {
		primaryStage = stage;
		BorderPane pane = new BorderPane();
		Scene scene = new Scene(pane, 480, 320);
		stage.setTitle("Comics Viewer");
		setIcon(stage, "res/application.png");

		images = new UnZipApi();
		imagesIndex = 0;
		pages = FX.getComboBox( this::gotoPage, null);
		imageView = new FxImageView(fontSize, keyHandle); //FxApi.getImageView();
		status = new Label("Ready");

		HBox hbox = FX.getHBox();
		FX.addHbox(hbox, FX.getButton("|<", (e) -> firstPage() ), false);
		FX.addHbox(hbox, FX.getButton("<", (e) -> prevPage() ), false);
		FX.addHbox(hbox, FX.getButton(">", (e) -> nextPage() ), false);
		FX.addHbox(hbox, FX.getButton(">|", (e) -> lastPage() ), false);
		FX.addHbox(hbox, FX.getButton("W", (e) -> fitWidth() ), false);
		FX.addHbox(hbox, FX.getButton("H", (e) -> fitHeight() ), false);
		FX.addHbox(hbox, FX.getButton("N", (e) -> fitNone() ), false);
		FX.addHbox(hbox, FX.getButton("+", (e) -> zoomIn() ), false);
		FX.addHbox(hbox, FX.getButton("-", (e) -> zoomOut() ), false);
		FX.addHbox(hbox, pages, false);
		VBox vbox = FX.getVBox();
		FX.addVbox(vbox, getMenuBar(), true);
		FX.addVbox(vbox, hbox, false);

		FX.setDropHandler(imageView.getLayout(), dropHandle);
		pane.setPadding(new Insets(0, 0, 0, 0));
		pane.setBottom(status);
		pane.setTop(vbox);
		pane.setCenter(imageView.getLayout());


		scene.setOnKeyPressed(ke -> {
			if( ke.getCode() == KeyCode.CONTROL ) { ctrl_pressed = true; }
			if( ke.getCode() == KeyCode.MINUS && ctrl_pressed ) { zoomOut(); }
			if( ke.getCode() == KeyCode.EQUALS && ctrl_pressed ) { zoomIn(); }
		});
		scene.setOnKeyReleased(ke -> {
			if( ke.getCode() == KeyCode.CONTROL ) { ctrl_pressed = false; }
		});
		scene.setOnScroll((ScrollEvent event) -> {
			double deltaY = event.getDeltaY();
			if( ctrl_pressed ) {
				if( deltaY > 0 ) { zoomIn(); }
				else { zoomOut(); }
			}
		});

		stage.setOnCloseRequest( (e) -> System.exit(0) ); //Platform->exit()
		stage.setScene(scene);
		stage.show();
	}

	void setIcon(Stage stage, String image) throws IOException {
		ClassLoader cl = getClass().getClassLoader();
		InputStream is = cl.getResourceAsStream(image);
		if( is != null ) {
			stage.getIcons().add(SwingFXUtils.toFXImage(ImageIO.read(is), null));
		}
	}

	Runnable runnableExit = new Runnable() {
		@Override public void run() {
			boolean result = yesnoDialog("Exit", "Do you want exit progrma ?");
			if( result ) { System.exit(0); }
		}
	};

	FxRunnable keyHandle = new FxRunnable() {
		@Override public void run(Object... object) {
			if( images != null ) {
				KeyCode e = (KeyCode)object[0];		

		        if ( e.equals(KeyCode.UP) ) {
		            System.out.println("Up Pressed");
		        }
		        if ( e.equals(KeyCode.DOWN) ) {
		            System.out.println("Down Pressed");
		        }
		        if ( e.equals(KeyCode.RIGHT) ) {
		            System.out.println("Right Pressed");
		            nextPage();
		        }
		        if ( e.equals(KeyCode.LEFT) ) {
		            System.out.println("Left Pressed");
		            prevPage();
		        }
		        if ( e.equals(KeyCode.PAGE_UP) ) {
		            System.out.println("PageUp Pressed");
		        }
		        if ( e.equals(KeyCode.PAGE_DOWN) ) {
		            System.out.println("PageDown Pressed");
		        }
		        if ( e.equals(KeyCode.HOME) ) {
		            System.out.println("Home Pressed");
		        }
		        if ( e.equals(KeyCode.END) ) {
		            System.out.println("End Pressed");
		        }
		        if ( e.equals(KeyCode.ENTER) ) {
		            System.out.println("Enter Pressed");
		        }

			}
		}
	};
	
	FxRunnable dropHandle = new FxRunnable() {
		@SuppressWarnings("unchecked")
		@Override public void run(Object... object) {
			if( object[0] instanceof List<?> ) {
				List<File> f = (List<File>)object[0];
				try {
					loadComicsFile(f.get(0).getAbsolutePath());
				} catch (Exception e) {
				}
			}
		}
	};

    public void loadComicsFile(String filePath) {
		if( new File(filePath).isFile() ) {
			try {
				images.load(filePath, "EUC_KR");
				pages.getItems().clear();
				for( int i = 0; i < images.size(); i++ ) {
					System.out.println( "- " + images.getName(i));
					pages.getItems().add(images.getName(i));
				}
				firstPage();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
    }

    public void setImage(int index) {
		try {
			imageView.setImage(images.unzipEntry(index));
			status.setText(" [ " + (index+1) + " / " + images.size() + " ] ");
		} catch (IOException e) {
			e.printStackTrace();
		}
    }
    
	public void firstPage() {
		imagesIndex = 0;
		pages.getSelectionModel().selectFirst();
	}
	
	public void lastPage() {
		imagesIndex = images.size() - 1;
		pages.getSelectionModel().selectLast();
	}
		
	public void nextPage() {
		if( imagesIndex < (images.size()-1) ) {
			imagesIndex++;
			pages.getSelectionModel().select(imagesIndex);
		}
	}
	
	public void prevPage() {
		if( imagesIndex > 0 ) {
			imagesIndex--;
			pages.getSelectionModel().select(imagesIndex);
		}
	}
	
	public void gotoPage() {
		int index  = pages.getSelectionModel().getSelectedIndex();
		if( index >= 0 && index <= (images.size()-1) ) {
			imagesIndex = index;
			setImage(imagesIndex);
		}
	}

	public void fitWidth() { imageView.fitWidth(); }
	public void fitHeight() { imageView.fitHeight(); }
	public void fitNone() { imageView.fitNone(); }
	public void zoomIn() { imageView.zoomIn(); }
	public void zoomOut() { imageView.zoomOut(); }


	MenuBar getMenuBar() {
		int fontSize = 12;
		MenuBar menubar = new MenuBar();
		menubar.getMenus().add(getFileMenu());
		menubar.setStyle("-fx-font: " + fontSize + " arial;");
		return menubar;
	}
	public Menu getFileMenu() {
		Menu menu = new Menu("File");
		MenuItem item = new MenuItem("Exit");
		item.setOnAction( (e) -> { System.exit(0); });
		menu.getItems().add(item);
		menu.setStyle("-fx-font: " + fontSize + " arial;");
		item.setStyle("-fx-font: " + fontSize + " arial;");
		return menu;
	}

	public boolean yesnoDialog(String title, String message) {
		Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
		alert.setTitle(title);
		alert.setHeaderText(null);
		alert.setContentText(message);
		alert.initModality(Modality.APPLICATION_MODAL);
		alert.initOwner(primaryStage);
		Optional<ButtonType> result = alert.showAndWait();
		if (result.get() == ButtonType.OK ){
			return true;
		} else {
			return false;
		}
	}
}

Java FX Plantuml Viewer

1. Java FX Plantuml Viewer

//Run > Run Configuration > Arguments > VM Arguments
//--module-path d:\Lib\Jar\javafx-sdk-16\lib --add-modules javafx.controls

package com.home.app;

import com.home.fx.*;
import com.home.java.IconApi;
import com.home.java.PUmlApi;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

public class FxPUmlViewer extends Application {

	static String plantuml_colors ="@startuml\ncolors\n@enduml";
	FxRichText textArea; //TextArea textArea;
	FxImageView imageView;
	List<BufferedImage> images;
	int imageIndex;
	SplitPane split;
	Label status;
	int fontSize = 12;
	Stage primaryStage;
	boolean ctrl_pressed = false;

	public static void main(String[] args) {
		launch(args);
	}

	@Override
	public void start(Stage stage) {
		primaryStage = stage;
		BorderPane pane = new BorderPane();
		Scene scene = new Scene(pane, 480, 320);
		stage.setTitle("PlantUml Viewer");
		stage.getIcons().add(SwingFXUtils.toFXImage(IconApi.getUmlImage(), null));

		textArea = new FxRichText(scene); //FxApi.getTextArea();
		imageView = new FxImageView(fontSize, keyHandle); //FxApi.getImageView();
		split = FX.getHSplitPane();
		status = new Label("Ready");

		FX.addSplitPane( split, textArea.getLayout());
		FX.addSplitPane( split, imageView.getLayout()); //FxApi.getScrollPane(imageView) );

		HBox hbox = FX.getHBox();
		FX.addHbox(hbox, FX.getButton("Color", (e) -> showColor() ), false);
		FX.addHbox(hbox, FX.getButton("Show", (e) -> showTextArea() ), false);
		FX.addHbox(hbox, FX.getButton("<", (e) -> showPrevImage() ), false);
		FX.addHbox(hbox, FX.getButton(">", (e) -> showNextImage() ), false);
		FX.addHbox(hbox, FX.getButton("W", (e) -> fitWidth() ), false);
		FX.addHbox(hbox, FX.getButton("H", (e) -> fitHeight() ), false);
		FX.addHbox(hbox, FX.getButton("N", (e) -> fitNone() ), false);
		FX.addHbox(hbox, FX.getButton("+", (e) -> zoomIn() ), false);
		FX.addHbox(hbox, FX.getButton("-", (e) -> zoomOut() ), false);
		VBox vbox = FX.getVBox();
		FX.addVbox(vbox, getMenuBar(), true);
		FX.addVbox(vbox, hbox, false);

		FX.setDropHandler(textArea, dropHandler);
		pane.setPadding(new Insets(0, 0, 0, 0));
		pane.setBottom(status);
		pane.setTop(vbox);
		pane.setCenter(split);

		scene.setOnKeyPressed(ke -> {
			if( ke.getCode() == KeyCode.CONTROL ) { ctrl_pressed = true; }
			if( ke.getCode() == KeyCode.MINUS && ctrl_pressed ) { zoomOut(); }
			if( ke.getCode() == KeyCode.EQUALS && ctrl_pressed ) { zoomIn(); }
		});
		scene.setOnKeyReleased(ke -> {
			if( ke.getCode() == KeyCode.CONTROL ) { ctrl_pressed = false; }
		});
		scene.setOnScroll((ScrollEvent event) -> {
			double deltaY = event.getDeltaY();
			if( ctrl_pressed ) {
				if( deltaY > 0 ) { zoomIn(); }
				else { zoomOut(); }
			}
		});

		stage.setOnCloseRequest( (e) -> System.exit(0) ); //Platform->exit()
		stage.setScene(scene);
		stage.show();
	}

	public void showImagePopup(String puml_text) {
		textArea.setText(puml_text);
		try {
			List<BufferedImage> images = PUmlApi.makeImage(puml_text);
			for( int i = 0; i < images.size(); i++ ) {
				SwingApi.showImage( "Diagram " + i, images.get(i));
			}
		} catch (IOException ex) {
			ex.printStackTrace();
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
	}

	public void showImage(int index) {
		if( images != null ) {
			imageView.setImage(SwingFXUtils.toFXImage(images.get(imageIndex), null));
			status.setText("" + (imageIndex+1) + "/" + images.size());
		}
	}

	public void renderImage(String puml_text) {
		try {
			//FxToast.show( primaryStage, "rendering ...", 1000, 250, 250);
			images = PUmlApi.makeImage(puml_text);
			imageIndex = 0;
			showImage( imageIndex );
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public void showPrevImage() {
		if( imageIndex > 0 ) {
			imageIndex -= 1;
			showImage( imageIndex );
		}
	}

	public void showNextImage() {
		if( imageIndex < images.size() -1 ) {
			imageIndex += 1;
			showImage( imageIndex );
		}
	}

	public void setText(String text) { textArea.setText(text); }
	void showTextArea() { renderImage(textArea.getText() ); }
	void showColor() { showImagePopup(plantuml_colors); }
	
	void getPasteHtmlAction() {
		if( Clipboard.getSystemClipboard().hasHtml() ) {
			textArea.clear();
			textArea.setText( Clipboard.getSystemClipboard().getHtml() );
		}
	}

	FxRunnable keyHandle = new FxRunnable() {
		@Override public void run(Object... object) {
			if( images != null ) {
				KeyCode e = (KeyCode)object[0];
				if ( e.equals(KeyCode.UP) ) {}
				if ( e.equals(KeyCode.DOWN) ) {}
				if ( e.equals(KeyCode.RIGHT) ) { showNextImage(); }
				if ( e.equals(KeyCode.LEFT) ) { showPrevImage(); }
				if ( e.equals(KeyCode.PAGE_UP) ) { }
				if ( e.equals(KeyCode.PAGE_DOWN) ) { }
				if ( e.equals(KeyCode.HOME) ) { }
				if ( e.equals(KeyCode.END) ) { }
				if ( e.equals(KeyCode.ENTER) ) { }
			}
		}
	};

	FxRunnable dropHandler = new FxRunnable() {
		@SuppressWarnings("unchecked")
		@Override public void run(Object... object) {
			if( object[0] instanceof List<?> ) {
				List<File> f = (List<File>)object[0];
				try {
					String text = new String (Files.readAllBytes( Paths.get((f.get(0).getAbsolutePath()))));
					textArea.setText(text);
					renderImage(text);
				} catch (Exception e) {
				}
			}
		}
	};
	public void fitWidth() { imageView.fitWidth(); }
	public void fitHeight() { imageView.fitHeight(); }
	public void fitNone() { imageView.fitNone(); }
	public void zoomIn() { imageView.zoomIn(); }
	public void zoomOut() { imageView.zoomOut(); }

	MenuBar getMenuBar() {
		int fontSize = 12;
		MenuBar menubar = new MenuBar();
		menubar.getMenus().add(getFileMenu());
		menubar.setStyle("-fx-font: " + fontSize + " arial;");
		return menubar;
	}
	public Menu getFileMenu() {
		Menu menu = new Menu("File");
		MenuItem item = new MenuItem("Exit");
		item.setOnAction( (e) -> { System.exit(0); });
		menu.getItems().add(item);
		menu.setStyle("-fx-font: " + fontSize + " arial;");
		item.setStyle("-fx-font: " + fontSize + " arial;");
		return menu;
	}
}

Java Swing API

 1. Java Swing API

package com.home.swing;

import com.home.java.IconApi;

import javax.swing.*;
import java.awt.*;
import java.io.IOException;

public class JS {

    public static void runLater( Runnable handler ) {
        SwingUtilities.invokeLater(handler);
    }
    public static Dimension getScreenSize() { return Toolkit.getDefaultToolkit().getScreenSize(); }
    public static Double getScreenSizeWidth() { return Toolkit.getDefaultToolkit().getScreenSize().getWidth(); }
    public static Double getScreenHeight() { return Toolkit.getDefaultToolkit().getScreenSize().getHeight(); }

    public static void setWindowIcon(Frame frame, String icon) { frame.setIconImage( IconApi.getBase64Image(icon) ); }
    public static void setWindowIconRes(Frame frame, String image) throws IOException {
        /*
        ClassLoader cl = frame.getClass().getClassLoader();
        InputStream is = cl.getResourceAsStream(image);
        if( is != null )
            frame.setIconImage(ImageIO.read(is));
        */
    }
    public static void setWindowTitle(Frame frame, String title) { frame.setTitle(title); }
    public static void setWindowCenter(Frame frame) {
        Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
        Dimension frm = frame.getSize();
        int xpos = (int)(screen.getWidth() / 2 - frm.getWidth() / 2);
        int ypos = (int)(screen.getHeight() / 2 - frm.getHeight() / 2);
        frame.setLocation(xpos, ypos);
    }
    public static void addTop(Frame frame, JComponent obj) { frame.add( obj, BorderLayout.NORTH); }
    public static void addCenter(Frame frame, JComponent obj) { frame.add( obj, BorderLayout.CENTER); }
    public static void addBottom(Frame frame, JComponent obj) { frame.add( obj, BorderLayout.SOUTH); }

    public static JMenuBar getMenuBar() {
        JMenuBar menubar = new JMenuBar();
        menubar.add(getFileMenu());
        return menubar;
    }
    public static JMenu getFileMenu() {
        JMenu menu = new JMenu("File");
        JMenuItem exit_item = new JMenuItem("Exit");
        exit_item.addActionListener((e) -> { System.exit(0); });
        menu.add(exit_item);
        return menu;
    }

    public static JSplitPane getHSplitPane(JComponent left, JComponent right) {
        JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, right);
        split.setDividerLocation(400);
        split.setResizeWeight(1);
        return split;
    }
    public static JSplitPane getVSplitPane(JComponent left, JComponent right) {
        JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT, left, right);
        split.setDividerLocation(400);
        split.setResizeWeight(1);
        return split;
    }

    public static JTabbedPane getTabbedPane() {
        JTabbedPane pane = new JTabbedPane();
        return pane;
    }
    public static void addTab( JTabbedPane pane, String name, JComponent node) {
        pane.add( name, node );
    }
}

Java Swing Tree API

 1. Java Swing Tree API

package com.home.swing;

import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

public class JSTree {

    public JTree ctrl;

    public JSTree(String rootName, Runnable handler) {
        DefaultMutableTreeNode root = new DefaultMutableTreeNode(rootName);
        /* root.add( new DefaultMutableTreeNode("하위1") );
        root.add( new DefaultMutableTreeNode("하위2") ); */
        ctrl = new JTree(root);
        if( handler != null ) {
            setTreeHandler(handler);
        }
    }
    public void showTreeRoot() {
        ctrl.setRootVisible(true);}
    public void hideTreeRoot() {
        ctrl.setRootVisible(false);}

    public void setTreeHandler(Runnable runnable) {
        ctrl.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener(){
            public void valueChanged(TreeSelectionEvent event) {
                runnable.run();
            }
        });
    }
    public String getTreeSelectionPath() {
        return ctrl.getSelectionModel().getSelectionPath().toString();
    }
    public void addTreeToRoot(String name) {
        DefaultTreeModel model = (DefaultTreeModel) ctrl.getModel();
        DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
        DefaultMutableTreeNode node = new DefaultMutableTreeNode(name);
        model.insertNodeInto(node, root, root.getChildCount());
        //model.insertNodeInto(node, root, model.getChildCount(root));
        model.reload(root);
    }
    public DefaultMutableTreeNode getSelectedNode() {
        return (DefaultMutableTreeNode)ctrl.getLastSelectedPathComponent();
    }
    public String[] getSelectedTreePath() {
        TreePath path = ctrl.getSelectionPath();
        if( path != null ) {
            int count = path.getPathCount();
            String[] nodes = new String[count];
            for( int i = 0; i < count; i++ ) {
                nodes[count-i-1] = path.getLastPathComponent().toString();
                path = path.getParentPath();
            }
            return nodes;
        }
        return null;
    }
}