pvbemu/src/desktop/util/Util.java

396 lines
14 KiB
Java

package util;
// Java imports
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import java.net.*;
import java.nio.charset.*;
import java.nio.file.*;
import java.util.*;
import javax.imageio.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.plaf.basic.*;
// General utility methods
public final class Util {
// This class cannot be instantiated
private Util() { }
///////////////////////////////////////////////////////////////////////////
// Classes //
///////////////////////////////////////////////////////////////////////////
// Event listener interfaces
public interface OnClose { void call(WindowEvent e); }
public interface OnClose2 { void call(InternalFrameEvent e); }
public interface OnFocus { void call(FocusEvent e); }
public interface OnKey { void call(KeyEvent e); }
public interface OnMouse { void call(MouseEvent e); }
public interface OnResize { void call(ComponentEvent e); }
public interface OnVisible { void call(AncestorEvent e); }
// Data class for byte-order marks
private static class BOM {
byte[] mark;
Charset set;
BOM(byte[] m, Charset s) { mark = m; set = s; }
}
// Font metrics wrapper
public static class Font extends java.awt.Font {
public final FontMetrics metrics;
public Font(java.awt.Font font) {
super(font);
metrics = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)
.getGraphics().getFontMetrics(this);
}
}
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Directory from which the JVM was initialized
public static final File PWD = new File(System.getProperty("user.dir"));
// Text file byte-order marks
private static final BOM[] BOMS = {
new BOM(new byte[] { -17, -69, -65 }, StandardCharsets.UTF_8 ),
new BOM(new byte[] { - 2, - 1 }, StandardCharsets.UTF_16BE),
new BOM(new byte[] { - 1, - 2 }, StandardCharsets.UTF_16LE),
};
// Available font families
private static final String[] FONTS;
// Filesystem state manager for the current .jar (if any)
private static final FileSystem JARFS;
// Static initializer
static {
// Font families
var fonts = GraphicsEnvironment
.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
Arrays.sort(fonts, String.CASE_INSENSITIVE_ORDER);
FONTS = fonts;
// .jar filesystem
FileSystem fs = null;
try {
fs = FileSystems.newFileSystem(
new URI("jar:" + Util.class.getProtectionDomain()
.getCodeSource().getLocation().toString()),
new HashMap<String, String>(),
Util.class.getClassLoader()
);
} catch (Exception e) { }
JARFS = fs;
}
///////////////////////////////////////////////////////////////////////////
// Static Methods //
///////////////////////////////////////////////////////////////////////////
// Apply an alpha value to a Color object
public static Color alpha(Color color, float alpha) {
int a = (int) Math.round(alpha * 255);
return color == null || a < 0 || a > 255 ? null :
new Color(color.getRGB() & 0x00FFFFFF | a << 24, true);
}
// Blend one color into another
public static Color blend(Color front, Color back, float alpha) {
// Error checking
if (front == null || back == null ||
!Float.isFinite(alpha) || alpha < 0 || alpha > 1)
return null;
// Decompose components
var colors = new Color[] { front, back };
var argb = new float[3][4];
for (int x = 0; x < 2; x++) {
int bits = colors[x].getRGB();
for (int y = 3; y >= 0; y++, bits >>= 8)
argb[x][y] = (bits & 0xFF) / 255.0f;
}
// Combine the colors
argb[2][0] = argb[0][0] + argb[1][0] * (1 - argb[0][0]);
for (int x = 1; x < 4; x++)
argb[2][x] = (
argb[0][x] * argb[0][0] +
argb[1][x] * argb[1][0] * (1 - argb[0][0])
) / argb[2][0];
// Produce the resulting Color object
int bits = 0;
for (int x = 0; x < 4; x++)
bits = bits << 8 | (int) Math.round(argb[2][x] * 255);
return new Color(bits, true);
}
// Read a file from disk
public static byte[] fileRead(File file) {
FileInputStream stream = null;
byte[] data = null;
try {
stream = new FileInputStream(file);
data = stream.readAllBytes();
} catch (Exception e) { }
try { stream.close(); } catch (Exception e) { }
return data;
}
// Read a file, first from disk, then from .jar
public static byte[] fileRead(String filename) {
InputStream stream = null;
// Open the file on disk
try { stream = new FileInputStream(filename); }
// File on disk could not be opened, so get resource from .jar
catch (Exception e) {
stream = Util.class.getResourceAsStream("/" + filename);
if (stream == null)
return null; // Resource in .jar could not be found
}
// Read the file data into memory
byte[] data = null;
try { data = stream.readAllBytes(); } catch (Exception e) { }
try { stream.close(); } catch (Exception e) { }
return data;
}
// Select a font family from a list, if avaiable
public static String fontFamily(String[] families) {
for (String family : families)
if (Arrays.binarySearch(FONTS, family,
String.CASE_INSENSITIVE_ORDER) >= 0)
return family;
return null;
}
// Read an image file as an icon
public static Icon iconRead(String filename) {
BufferedImage img = imageRead(filename);
return img == null ? null : new ImageIcon(img);
}
// Read an image file by filename
public static BufferedImage imageRead(String filename) {
try { return
ImageIO.read(new ByteArrayInputStream(fileRead(filename)));
} catch (Exception e) { return null; }
}
// Read an image file by handle
public static BufferedImage imageRead(File file) {
try { return imageRead(file.getAbsolutePath()); }
catch (Exception e) { return null; }
}
// Produce a list of files contained in a directory
// If the directory is not found on disk, looks for it in the .jar
public static String[] listFiles(String path) {
// Check for the directory on disk
var file = new File(path);
if (file.exists() && file.isDirectory())
return file.list();
// Not executing out of a .jar
if (JARFS == null)
return null;
// Check for the directory in the .jar
try {
var list = Files.list(JARFS.getPath("/" + path)).toArray();
var ret = new String[list.length];
for (int x = 0; x < list.length; x++) {
path = "/" + ((Path) list[x]).toString();
ret[x] = path.substring(path.lastIndexOf("/") + 1);
}
return ret;
} catch (Exception e) { }
// The directory was not found
return null;
}
// Produce a list of available font family names
public static String[] listFonts() {
var ret = new String[FONTS.length];
System.arraycopy(FONTS, 0, ret, 0, FONTS.length);
return ret;
}
// Produce a WindowListener with a functional interface
public static WindowListener onClose(OnClose close) {
return new WindowListener() {
public void windowActivated (WindowEvent e) { }
public void windowClosed (WindowEvent e) { }
public void windowDeactivated(WindowEvent e) { }
public void windowDeiconified(WindowEvent e) { }
public void windowIconified (WindowEvent e) { }
public void windowOpened (WindowEvent e) { }
public void windowClosing (WindowEvent e)
{ if (close != null) close.call(e); }
};
}
// Produce an InternalFrameListener with a functional interface
public static InternalFrameListener onClose2(OnClose2 close) {
return new InternalFrameListener() {
public void internalFrameActivated (InternalFrameEvent e) { }
public void internalFrameClosed (InternalFrameEvent e) { }
public void internalFrameDeactivated(InternalFrameEvent e) { }
public void internalFrameDeiconified(InternalFrameEvent e) { }
public void internalFrameIconified (InternalFrameEvent e) { }
public void internalFrameOpened (InternalFrameEvent e) { }
public void internalFrameClosing (InternalFrameEvent e)
{ if (close != null) close.call(e); }
};
}
// Produce a FocusListener with functional interfaces
public static FocusListener onFocus(OnFocus focus, OnFocus blur) {
return new FocusListener() {
public void focusGained(FocusEvent e)
{ if (focus != null) focus.call(e); }
public void focusLost (FocusEvent e)
{ if (blur != null) blur .call(e); }
};
}
// Produce a KeyListener with functional interfaces
public static KeyListener onKey(OnKey down, OnKey up) {
return new KeyListener() {
public void keyTyped (KeyEvent e) { }
public void keyPressed (KeyEvent e)
{ if (down != null) down.call(e); }
public void keyReleased(KeyEvent e)
{ if (up != null) up .call(e); }
};
}
// Produce a MouseMotionListener with a functional interface
public static MouseMotionListener onMouseMove(OnMouse move, OnMouse drag) {
return new MouseMotionListener() {
public void mouseMoved (MouseEvent e)
{ if (move != null) move.call(e); }
public void mouseDragged(MouseEvent e)
{ if (drag != null) drag.call(e); }
};
}
// Produce a MouseListener with functional interfaces
public static MouseListener onMouse(OnMouse down, OnMouse up) {
return new MouseListener() {
public void mouseClicked (MouseEvent e) { }
public void mouseEntered (MouseEvent e) { }
public void mouseExited (MouseEvent e) { }
public void mousePressed (MouseEvent e)
{ if (down != null) down.call(e); }
public void mouseReleased(MouseEvent e)
{ if (up != null) up .call(e); }
};
}
// Produce a ComponentListener with a functional interface
public static ComponentListener onResize(OnResize resize) {
return new ComponentListener() {
public void componentHidden (ComponentEvent e) { }
public void componentMoved (ComponentEvent e) { }
public void componentShown (ComponentEvent e) { }
public void componentResized(ComponentEvent e)
{ if (resize != null) resize.call(e); }
};
}
// Produce an AncestorListener using functional interfaces
public static AncestorListener onVisible(OnVisible show, OnVisible hide) {
return new AncestorListener() {
public void ancestorMoved (AncestorEvent e) { }
public void ancestorAdded (AncestorEvent e)
{ if (show != null) show.call(e); }
public void ancestorRemoved(AncestorEvent e)
{ if (hide != null) hide.call(e); }
};
}
// Configure the Swing look-and-feel with the system theme
public static boolean setSystemLAF() {
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
return true;
} catch (Exception e) { return false; }
}
// Produce a JSplitPane with an un-styled divider
public static JSplitPane splitPane(int orientation) {
var split = new JSplitPane(orientation, null, null);
split.setContinuousLayout(true);
split.setDividerSize(2);
split.setUI(new BasicSplitPaneUI() {
public BasicSplitPaneDivider createDefaultDivider() {
return new BasicSplitPaneDivider(this) {
public void setBorder(Border b) { }
};
}
});
split.setBorder(null);
return split;
}
// Read a text file, accounting for BOMs
public static String textRead(String filename) {
// Read the file
byte[] data = fileRead(filename);
if (data == null)
return null;
// Configure working variables
byte[] mark = null;
Charset set = StandardCharsets.ISO_8859_1; // Default charset
// Check for a byte-order mark
outer: for (BOM bom : BOMS) {
if (data.length < bom.mark.length)
continue;
for (int x = 0; x < bom.mark.length; x++)
if (data[x] != bom.mark[x])
continue outer;
mark = bom.mark;
set = bom.set;
break;
}
// Remove the byte-order mark from the data
if (mark != null) {
byte[] temp = new byte[data.length - mark.length];
System.arraycopy(data, mark.length, temp, 0, temp.length);
data = temp;
}
// Produce a string
return new String(data, set);
}
}