RestorableFrame.java
     1: //========================================================================================
     2: //  RestorableFrame.java
     3: //    en:Restorable frame -- Frames that can save and restore position and size
     4: //    ja:リストアラブルフレーム -- 位置とサイズの保存と復元ができるフレーム
     5: //  Copyright (C) 2003-2025 Makoto Kamada
     6: //
     7: //  This file is part of the XEiJ (X68000 Emulator in Java).
     8: //  You can use, modify and redistribute the XEiJ if the conditions are met.
     9: //  Read the XEiJ License for more details.
    10: //  https://stdkmd.net/xeij/
    11: //========================================================================================
    12: 
    13: package xeij;
    14: 
    15: import java.awt.*;
    16: import java.awt.event.*;
    17: import java.awt.image.*;  //BufferedImage
    18: import java.util.*;  //HashMap
    19: import javax.swing.*;  //JFrame
    20: 
    21: public class RestorableFrame extends JFrame {
    22: 
    23:   static final boolean DEBUG = false;
    24: 
    25:   //  フレーム作成→復元する値の設定→復元→変化の記録→保存する値の取得
    26:   //  または
    27:   //  復元する値の設定→フレーム作成→復元→変化の記録→保存する値の取得
    28: 
    29:   static class Info {
    30: 
    31:     String key;
    32:     RestorableFrame frame;  //フレーム
    33:     int x, y;  //位置
    34:     int width, height;  //サイズ
    35:     int state;  //状態
    36:     boolean opened;  //開いているか。開くのは個々のフレームで行う
    37: 
    38:     //new Info (frame)
    39:     //  コンストラクタ
    40:     Info (String key, RestorableFrame frame) {
    41:       if (DEBUG) {
    42:         System.out.println ("Info " + key + (frame == null ? " null" : " frame"));
    43:       }
    44:       this.key = key;
    45:       this.frame = frame;
    46:     }  //Info
    47: 
    48:     void setFrame (RestorableFrame frame) {
    49:       if (DEBUG) {
    50:         System.out.println ("Info.setFrame" + key);
    51:       }
    52:       this.frame = frame;
    53:     }
    54: 
    55:     //set (rect, state, opened)
    56:     //  復元する値を設定する
    57:     void set (int[] rect, int state, boolean opened) {
    58:       if (DEBUG) {
    59:         System.out.println ("Info.set " + key);
    60:       }
    61:       x = rect[0];  //位置
    62:       y = rect[1];
    63:       width = rect[2];  //サイズ
    64:       height = rect[3];
    65:       this.state = state;  //状態
    66:       this.opened = opened;  //開いているか
    67:     }  //set
    68: 
    69:     //restore ()
    70:     //  復元する。開くのは個々のフレームで行う
    71:     void restore () {
    72:       if (DEBUG) {
    73:         System.out.println ("Info.restore " + key);
    74:       }
    75:       //フレームの上端の48x48が収まる画面を探す
    76:     test:
    77:       {
    78:         Rectangle testBounds = new Rectangle (x, y, width, 48);  //フレームの上端のwidthx48
    79:         for (GraphicsDevice gd : GraphicsEnvironment.getLocalGraphicsEnvironment ().getScreenDevices ()) {
    80:           for (GraphicsConfiguration gc : gd.getConfigurations ()) {
    81:             Rectangle intersectionBounds = testBounds.intersection (gc.getBounds ());  //フレームの上端のwidthx48と画面が重なっている範囲
    82:             if (48 <= intersectionBounds.width && 48 <= intersectionBounds.height) {  //48x48以上ある
    83:               //フレームの上端の48x48が収まる画面が見つかった
    84:               break test;  //そのまま復元する
    85:             }
    86:           }  //for gc
    87:         }  //for gd
    88:         //フレームの上端の48x48が収まる画面が見つからなかった
    89:         //フレームの上端の48x48がデフォルトの画面に収まるように位置を調整する
    90:         GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment ().getDefaultScreenDevice ().getDefaultConfiguration ();
    91:         Rectangle s = gc.getBounds ();  //画面のレクタングル。左上が(0,0)とは限らない
    92:         x = Math.min (x, s.x + s.width - 48);  //フレームの左端は画面の右端-48より左にあること
    93:         if (0 < width) {
    94:           x = Math.max (x + width, s.x + 48) - width;  //フレームの右端は画面の左端+48より右にあること
    95:         }
    96:         y = Math.min (y, s.y + s.height - 48);  //フレームの上端は画面の下端-48より上にあること
    97:         y = Math.max (y, s.y);  //フレームの上端は画面の上端より下にあること。タイトルバーが見えないと困るので下端ではなく上端の条件
    98:       }  //test
    99:       //復元する
   100:       frame.setLocation (x, y);  //位置
   101:       if (0 < width && 0 < height) {
   102:         frame.setSize (width, height);  //サイズ
   103:         frame.setPreferredSize (new Dimension (width, height));  //setSizeだけだとパネルのsetPreferredSizeに負けてしまう
   104:       }
   105:       frame.setExtendedState (state);  //状態
   106:       //変化の記録を開始する
   107:       frame.addComponentListener (new ComponentAdapter () {
   108:         @Override public void componentMoved (ComponentEvent ce) {
   109:           record (false);  //記録する
   110:         }
   111:         @Override public void componentResized (ComponentEvent ce) {
   112:           record (false);  //記録する
   113:         }
   114:       });
   115:       frame.addWindowStateListener (new WindowStateListener () {
   116:         @Override public void windowStateChanged (WindowEvent we) {
   117:           record (false);  //記録する
   118:         }
   119:       });
   120:       frame.addWindowListener (new WindowAdapter () {
   121:         @Override public void windowClosing (WindowEvent we) {
   122:           //HIDE_ON_CLOSEのときclosedは呼び出されないがclosingは呼び出される
   123:           //isShowing()はtrue
   124:           record (true);  //記録する
   125:         }
   126:         @Override public void windowOpened (WindowEvent we) {
   127:           //isShowing()はtrue
   128:           record (false);  //記録する
   129:         }
   130:       });
   131:     }  //restore
   132: 
   133:     //record (closing)
   134:     //  変化を記録する
   135:     void record (boolean closing) {
   136:       if (DEBUG) {
   137:         System.out.println ("Info.record " + key);
   138:       }
   139:       if (frame != GraphicsEnvironment.getLocalGraphicsEnvironment ().getDefaultScreenDevice ().getFullScreenWindow ()) {  //全画面でない
   140:         boolean opened = !closing && frame.isShowing ();  //開いているか
   141:         if (opened) {  //開いている
   142:           Point p = frame.getLocationOnScreen ();  //位置
   143:           Dimension d = frame.getSize ();  //サイズ
   144:           int state = frame.getExtendedState ();  //状態
   145:           if ((state & (Frame.ICONIFIED | Frame.MAXIMIZED_HORIZ)) == 0) {  //アイコン化または水平方向に最大化されていない
   146:             x = p.x;  //X座標
   147:             width = d.width;  //幅
   148:           }
   149:           if ((state & (Frame.ICONIFIED | Frame.MAXIMIZED_VERT)) == 0) {  //アイコン化または垂直方向に最大化されていない
   150:             y = p.y;  //Y座標
   151:             height = d.height;  //高さ
   152:           }
   153:           this.state = state;  //状態
   154:         }
   155:         this.opened = opened;  //開いているか
   156:       }
   157:     }  //record
   158: 
   159:     //rect = getRect ()
   160:     //  位置とサイズを取得する
   161:     int[] getRect () {
   162:       if (DEBUG) {
   163:         System.out.println ("Info.getRect " + key);
   164:       }
   165:       return new int[] { x, y, width, height };
   166:     }  //getRect
   167: 
   168:     //state = getState ()
   169:     //  状態を取得する
   170:     int getState () {
   171:       if (DEBUG) {
   172:         System.out.println ("Info.getState " + key);
   173:       }
   174:       return state;
   175:     }  //getState
   176: 
   177:     //opened = getOpened ()
   178:     //  開いているかを取得する
   179:     boolean getOpened () {
   180:       if (DEBUG) {
   181:         System.out.println ("Info.getOpened " + key);
   182:       }
   183:       return opened;
   184:     }  //getOpened
   185: 
   186:     //image = capture ()
   187:     //  キャプチャする
   188:     BufferedImage capture () {
   189:       if (DEBUG) {
   190:         System.out.println ("Info.capture " + key);
   191:       }
   192:       if (frame != null) {  //フレームがある
   193:         try {
   194:           Point p = frame.getLocationOnScreen ();
   195:           Dimension d = frame.getSize ();
   196:           if (0 < d.width && 0 < d.height) {
   197:             return new Robot ().createScreenCapture (new Rectangle (p.x, p.y, d.width, d.height));
   198:           }
   199:         } catch (Exception e) {
   200:           //getLocationOnScreen IllegalComponentStateException 画面にない
   201:           //Robot,AWTException
   202:           //Robot,createScreenCapture SecurityException 権限がない
   203:           //createScreenCapture IllegalArgumentException サイズが0以下
   204:         }
   205:       }
   206:       return null;
   207:     }  //capture
   208: 
   209:   }  //class Info
   210: 
   211: 
   212: 
   213:   static HashMap<String,Info> rfmMap = new HashMap<String,Info> ();
   214: 
   215:   //rfmSet (key, rect, state, opened)
   216:   //  復元する値を設定する
   217:   public static void rfmSet (String key, int[] rect, int state, boolean opened) {
   218:     if (DEBUG) {
   219:       System.out.println ("rfmSet " + key);
   220:     }
   221:     Info i = rfmMap.get (key);
   222:     if (i == null) {  //フレームが作成されていない
   223:       i = new Info (key, null);
   224:       rfmMap.put (key, i);
   225:       i.set (rect, state, opened);  //復元する値を設定する
   226:     } else {  //フレームが作成されている
   227:       i.set (rect, state, opened);  //復元する値を設定する
   228:       i.restore ();  //復元する
   229:     }
   230:   }  //rfmSet
   231: 
   232:   //x = rfmGetRect (key)
   233:   //  位置とサイズを取得する
   234:   public static int[] rfmGetRect (String key) {
   235:     if (DEBUG) {
   236:       System.out.println ("rfmGetRect " + key);
   237:     }
   238:     return rfmMap.get (key).getRect ();
   239:   }  //rfmGetRect
   240: 
   241:   //state = rfmGetState (key)
   242:   //  状態を取得する
   243:   public static int rfmGetState (String key) {
   244:     if (DEBUG) {
   245:       System.out.println ("rfmGetState " + key);
   246:     }
   247:     return rfmMap.get (key).getState ();
   248:   }  //rfmGetState
   249: 
   250:   //opened = rfmGetOpened (key)
   251:   //  開いているかを取得する
   252:   public static boolean rfmGetOpened (String key) {
   253:     if (DEBUG) {
   254:       System.out.println ("rfmGetOpened " + key);
   255:     }
   256:     return rfmMap.get (key).getOpened ();
   257:   }  //rfmGetOpened
   258: 
   259: 
   260: 
   261:   Info info;
   262: 
   263:   //new RestorableFrame (key, title)
   264:   //  コンストラクタ
   265:   public RestorableFrame (String key, String title) {
   266:     super (title, GraphicsEnvironment.getLocalGraphicsEnvironment ().getDefaultScreenDevice ().getDefaultConfiguration ());
   267:     if (DEBUG) {
   268:       System.out.println ("RestorableFrame " + key);
   269:     }
   270:     Info i = rfmMap.get (key);
   271:     if (i == null) {  //復元する値が設定されていない
   272:       info = i = new Info (key, this);
   273:     } else {  //復元する値が設定されている
   274:       info = i;
   275:       i.setFrame (this);
   276:       i.restore ();  //復元する。開くのは個々のフレームで行う
   277:     }
   278:   }  //RestorableFrame
   279: 
   280:   //image = rfmCapture (key)
   281:   //  フレームをキャプチャする
   282:   public static BufferedImage rfmCapture (String key) {
   283:     if (DEBUG) {
   284:       System.out.println ("rfmCapture " + key);
   285:     }
   286:     return rfmMap.get (key).capture ();
   287:   }  //rfmCapture
   288: 
   289: 
   290: 
   291: }  //class RestorableFrame
   292: 
   293: 
   294: