xeij/RestorableFrame.java
//========================================================================================
// RestorableFrame.java
// en:Restorable frame -- Frames that can save and restore position and size
// ja:リストアラブルフレーム -- 位置とサイズの保存と復元ができるフレーム
// Copyright (C) 2003-2025 Makoto Kamada
//
// This file is part of the XEiJ (X68000 Emulator in Java).
// You can use, modify and redistribute the XEiJ if the conditions are met.
// Read the XEiJ License for more details.
// https://stdkmd.net/xeij/
//========================================================================================
package xeij;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*; //BufferedImage
import java.util.*; //HashMap
import javax.swing.*; //JFrame
public class RestorableFrame extends JFrame {
static final boolean DEBUG = false;
// フレーム作成→復元する値の設定→復元→変化の記録→保存する値の取得
// または
// 復元する値の設定→フレーム作成→復元→変化の記録→保存する値の取得
static class Info {
String key;
RestorableFrame frame; //フレーム
int x, y; //位置
int width, height; //サイズ
int state; //状態
boolean opened; //開いているか。開くのは個々のフレームで行う
//new Info (frame)
// コンストラクタ
Info (String key, RestorableFrame frame) {
if (DEBUG) {
System.out.println ("Info " + key + (frame == null ? " null" : " frame"));
}
this.key = key;
this.frame = frame;
} //Info
void setFrame (RestorableFrame frame) {
if (DEBUG) {
System.out.println ("Info.setFrame" + key);
}
this.frame = frame;
}
//set (rect, state, opened)
// 復元する値を設定する
void set (int[] rect, int state, boolean opened) {
if (DEBUG) {
System.out.println ("Info.set " + key);
}
x = rect[0]; //位置
y = rect[1];
width = rect[2]; //サイズ
height = rect[3];
this.state = state; //状態
this.opened = opened; //開いているか
} //set
//restore ()
// 復元する。開くのは個々のフレームで行う
void restore () {
if (DEBUG) {
System.out.println ("Info.restore " + key);
}
//フレームの上端の48x48が収まる画面を探す
test:
{
Rectangle testBounds = new Rectangle (x, y, width, 48); //フレームの上端のwidthx48
for (GraphicsDevice gd : GraphicsEnvironment.getLocalGraphicsEnvironment ().getScreenDevices ()) {
for (GraphicsConfiguration gc : gd.getConfigurations ()) {
Rectangle intersectionBounds = testBounds.intersection (gc.getBounds ()); //フレームの上端のwidthx48と画面が重なっている範囲
if (48 <= intersectionBounds.width && 48 <= intersectionBounds.height) { //48x48以上ある
//フレームの上端の48x48が収まる画面が見つかった
break test; //そのまま復元する
}
} //for gc
} //for gd
//フレームの上端の48x48が収まる画面が見つからなかった
//フレームの上端の48x48がデフォルトの画面に収まるように位置を調整する
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment ().getDefaultScreenDevice ().getDefaultConfiguration ();
Rectangle s = gc.getBounds (); //画面のレクタングル。左上が(0,0)とは限らない
x = Math.min (x, s.x + s.width - 48); //フレームの左端は画面の右端-48より左にあること
if (0 < width) {
x = Math.max (x + width, s.x + 48) - width; //フレームの右端は画面の左端+48より右にあること
}
y = Math.min (y, s.y + s.height - 48); //フレームの上端は画面の下端-48より上にあること
y = Math.max (y, s.y); //フレームの上端は画面の上端より下にあること。タイトルバーが見えないと困るので下端ではなく上端の条件
} //test
//復元する
frame.setLocation (x, y); //位置
if (0 < width && 0 < height) {
frame.setSize (width, height); //サイズ
frame.setPreferredSize (new Dimension (width, height)); //setSizeだけだとパネルのsetPreferredSizeに負けてしまう
}
frame.setExtendedState (state); //状態
//変化の記録を開始する
frame.addComponentListener (new ComponentAdapter () {
@Override public void componentMoved (ComponentEvent ce) {
record (false); //記録する
}
@Override public void componentResized (ComponentEvent ce) {
record (false); //記録する
}
});
frame.addWindowStateListener (new WindowStateListener () {
@Override public void windowStateChanged (WindowEvent we) {
record (false); //記録する
}
});
frame.addWindowListener (new WindowAdapter () {
@Override public void windowClosing (WindowEvent we) {
//HIDE_ON_CLOSEのときclosedは呼び出されないがclosingは呼び出される
//isShowing()はtrue
record (true); //記録する
}
@Override public void windowOpened (WindowEvent we) {
//isShowing()はtrue
record (false); //記録する
}
});
} //restore
//record (closing)
// 変化を記録する
void record (boolean closing) {
if (DEBUG) {
System.out.println ("Info.record " + key);
}
if (frame != GraphicsEnvironment.getLocalGraphicsEnvironment ().getDefaultScreenDevice ().getFullScreenWindow ()) { //全画面でない
boolean opened = !closing && frame.isShowing (); //開いているか
if (opened) { //開いている
Point p = frame.getLocationOnScreen (); //位置
Dimension d = frame.getSize (); //サイズ
int state = frame.getExtendedState (); //状態
if ((state & (Frame.ICONIFIED | Frame.MAXIMIZED_HORIZ)) == 0) { //アイコン化または水平方向に最大化されていない
x = p.x; //X座標
width = d.width; //幅
}
if ((state & (Frame.ICONIFIED | Frame.MAXIMIZED_VERT)) == 0) { //アイコン化または垂直方向に最大化されていない
y = p.y; //Y座標
height = d.height; //高さ
}
this.state = state; //状態
}
this.opened = opened; //開いているか
}
} //record
//rect = getRect ()
// 位置とサイズを取得する
int[] getRect () {
if (DEBUG) {
System.out.println ("Info.getRect " + key);
}
return new int[] { x, y, width, height };
} //getRect
//state = getState ()
// 状態を取得する
int getState () {
if (DEBUG) {
System.out.println ("Info.getState " + key);
}
return state;
} //getState
//opened = getOpened ()
// 開いているかを取得する
boolean getOpened () {
if (DEBUG) {
System.out.println ("Info.getOpened " + key);
}
return opened;
} //getOpened
//image = capture ()
// キャプチャする
BufferedImage capture () {
if (DEBUG) {
System.out.println ("Info.capture " + key);
}
if (frame != null) { //フレームがある
try {
Point p = frame.getLocationOnScreen ();
Dimension d = frame.getSize ();
if (0 < d.width && 0 < d.height) {
return new Robot ().createScreenCapture (new Rectangle (p.x, p.y, d.width, d.height));
}
} catch (Exception e) {
//getLocationOnScreen IllegalComponentStateException 画面にない
//Robot,AWTException
//Robot,createScreenCapture SecurityException 権限がない
//createScreenCapture IllegalArgumentException サイズが0以下
}
}
return null;
} //capture
} //class Info
static HashMap<String,Info> rfmMap = new HashMap<String,Info> ();
//rfmSet (key, rect, state, opened)
// 復元する値を設定する
public static void rfmSet (String key, int[] rect, int state, boolean opened) {
if (DEBUG) {
System.out.println ("rfmSet " + key);
}
Info i = rfmMap.get (key);
if (i == null) { //フレームが作成されていない
i = new Info (key, null);
rfmMap.put (key, i);
i.set (rect, state, opened); //復元する値を設定する
} else { //フレームが作成されている
i.set (rect, state, opened); //復元する値を設定する
i.restore (); //復元する
}
} //rfmSet
//x = rfmGetRect (key)
// 位置とサイズを取得する
public static int[] rfmGetRect (String key) {
if (DEBUG) {
System.out.println ("rfmGetRect " + key);
}
return rfmMap.get (key).getRect ();
} //rfmGetRect
//state = rfmGetState (key)
// 状態を取得する
public static int rfmGetState (String key) {
if (DEBUG) {
System.out.println ("rfmGetState " + key);
}
return rfmMap.get (key).getState ();
} //rfmGetState
//opened = rfmGetOpened (key)
// 開いているかを取得する
public static boolean rfmGetOpened (String key) {
if (DEBUG) {
System.out.println ("rfmGetOpened " + key);
}
return rfmMap.get (key).getOpened ();
} //rfmGetOpened
Info info;
//new RestorableFrame (key, title)
// コンストラクタ
public RestorableFrame (String key, String title) {
super (title, GraphicsEnvironment.getLocalGraphicsEnvironment ().getDefaultScreenDevice ().getDefaultConfiguration ());
if (DEBUG) {
System.out.println ("RestorableFrame " + key);
}
Info i = rfmMap.get (key);
if (i == null) { //復元する値が設定されていない
info = i = new Info (key, this);
} else { //復元する値が設定されている
info = i;
i.setFrame (this);
i.restore (); //復元する。開くのは個々のフレームで行う
}
} //RestorableFrame
//image = rfmCapture (key)
// フレームをキャプチャする
public static BufferedImage rfmCapture (String key) {
if (DEBUG) {
System.out.println ("rfmCapture " + key);
}
return rfmMap.get (key).capture ();
} //rfmCapture
} //class RestorableFrame