    001/*
    002 * Copyright (c) 2009 The openGion Project.
    003 *
    004 * Licensed under the Apache License, Version 2.0 (the "License");
    005 * you may not use this file except in compliance with the License.
    006 * You may obtain a copy of the License at
    007 *
    008 *     http://www.apache.org/licenses/LICENSE-2.0
    009 *
    010 * Unless required by applicable law or agreed to in writing, software
    011 * distributed under the License is distributed on an "AS IS" BASIS,
    012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
    013 * either express or implied. See the License for the specific language
    014 * governing permissions and limitations under the License.
    015 */
    016package org.opengion.fukurou.util;
    017
    018import org.opengion.fukurou.system.OgRuntimeException ;         // 6.4.2.0 (2016/01/29)
    019import java.awt.Rectangle;
    020import java.awt.Robot;
    021import java.awt.image.BufferedImage;
    022import java.awt.AWTException;
    023import javax.imageio.ImageIO;
    024
    025import java.awt.Toolkit;
    026import java.awt.datatransfer.Clipboard;
    027import java.awt.datatransfer.DataFlavor;
    028import java.awt.datatransfer.StringSelection;
    029import java.awt.datatransfer.FlavorListener;
    030import java.awt.datatransfer.FlavorEvent;
    031import java.awt.datatransfer.UnsupportedFlavorException;
    032import java.io.IOException;
    033import java.io.File;
    034
    035/**
    036 * DisplayCapture.java は、画面イメージをキャプチャして、ファイルに書き出すためのクラスです。
    037 *
    038 * 基本的な使い方は、main メソッドから立ち上げて、クリップボードの状態を監視します。
    039 * クリップボードに、"GUI:画面ID xxxxx.jsp" 形式の文字が書き込まれると、flavorsChanged イベントが
    040 * 発生して、画面を、ファイルに書き出す処理が実行されます。
    041 * エンジンの機能と連動すれば、画面IDやファイル名をクリップボード経由でこのアプリケーションに
    042 * 渡すことで、画面操作を行うだけで、自動的に画面キャプチャを行うことが可能です。
    043 *
    044 * エンジンでは、jsp/indexc.jsp に、この機能が組み込まれているため、gf\BAT\displayCapture の
    045 * DisplayCapture.bat を起動後、アプリケーションを、jsp/indexc.jsp で呼び出せば、自動的に
    046 * 画面のキャプチャを開始できます。
    047 * キャプチャは、全画面のみですので、IEを最大に広げて操作してください。
    048 * また、｢Prnt Scrn｣ボタンにも対応していますので、操作中やポップアップ等の非自動化の
    049 * 画面キャプチャも、手動で取得できます。
    050 *
    051 * 起動時の引数に応じて、処理を制御することが可能です。
    052 *
    053 * 書き出すフォルダは、BASE_DIR で指定します。
    054 *  すべてのキャプチャ画像は、ベースフォルダ に集約して保存します。
    055 *  キャプチャ画像の情報は、出力されるファイル名に反映されます。ファイル名の形式も２種類あり、
    056 *  キャプチャ順と、画面ID順が指定できます。
    057 *  初期値は、Java起動時のフォルダになります。
    058 *
    059 * 書き出すファイル名の初期形式は、firstID の設定により異なります。
    060 *   firstID を gui に設定した場合
    061 *      ベースフォルダでフォルダを作成し、その中に、画面ID_JSPファイル名_連番.画像形式 ファイルを作成します。
    062 *      画面ID 単位に、画面のキャプチャを整理、使用したい場合に便利です。
    063 *      ファイルのタイムスタンプ(作成時刻)で並び替えを行えば、キャプチャ順に並び替えできます。
    064 *   firstID を seq に設定した場合
    065 *      ベースフォルダでフォルダを作成し、その中に、連番_画面ID_JSPファイル名.画像形式 ファイルを作成します。
    066 *      ファイルは、キャプチャされた順番に、画面IDも混在して作成されます。つまり、ファイル名の順番に
    067 *      再生すれば、リンクや他のシステムとの連携などで、画面が行き来しても、作業の順番にキャプチャできて
    068 *      いる事になります。
    069 *
    070 * このクラスは、これらを実現するために利用している、static メソッドをいくつか持っています。
    071 *   BufferedImage doCapture()
    072 *      画面イメージをキャプチャします。これは、全画面です。
    073 *   void saveImage( File saveFile, BufferedImage img, String imgType )
    074 *      指定のファイルに、画面イメージを書き出します。
    075 *      imgType に、画像の種類(png|gif|jpg)を指定します(初期値:png)。
    076 *   String getClipboard()
    077 *      現在のクリップボードの値を取り出します。ここでは、文字列のみ取り出すことが可能です。
    078 *      このメソッドの特徴的なところは、PrintScreenなどの文字列以外の値をクリップボードにセット
    079 *      した場合に、"GUI:PRINT SCREEN.img" という文字列を返すところです。つまり、その場合は、
    080 *      全画面のキャプチャが行われるという事です。
    081 *   void setClipboard( String txt )
    082 *      クリップボードに、文字列をセットします。
    083 *
    084 * このクラスが実装している FlavorListener は、クリップボードの"値"の更新には追従していません。
    085 * 内部の Transferable オブジェクトが変更された場合に、flavorsChanged メソッドが呼び出されます。
    086 * つまり、一度セットされた文字型データは、取り出した後、別のTransferable オブジェクトに変更して
    087 * おかないと、次の文字列の変更が拾えなくなります。また、この別のTransferableオブジェクトの
    088 * 設定で、再び、イベントが発生するので、そのままでは、無限ループになってしまいます。
    089 * そこで、少し、トリッキーなのですが、setClipboard( String ) すると、再びイベントが呼び出され
    090 * ないように、取得した文字列の先頭が、"GUI:" で始まる場合のみ、再設定するようにしています。
    091 *
    092 * Usage: java org.opengion.fukurou.util.DisplayCapture
    093 *                   [BASE_DIR] [firstID(seq|gui)] [imageFormat(png|gif|jpg)] [startCnt(100)]
    094 *
    095 *     args[0]  BASE_DIR    : キャプチャファイルをセーブするベースとなるディレクトリ(初期値:起動フォルダ)
    096 *     args[1]  firstID     : キャプチャ画像をセーブするファイル方式を指定します(初期値:seq)
    097 *                            gui (画面ID_JSPファイル名_連番.画像形式)
    098 *                            seq (連番_画面ID_JSPファイル名.画像形式)
    099 *     args[2]  imageFormat : 作成するイメージの形式。png|gif|jpg のどれか(初期値:png)
    100 *     args[3]  startCnt    : セーブファイル名をユニークにするためのカウント(初期値:100)
    101 *
    102 * この実装は同期化されません。
    103 *
    104 * @og.rev 5.1.7.0 (2010/06/01) 新規追加
    105 * @og.rev 5.2.1.0 (2010/10/01) 実用性を重視した改修
    106 *
    107 * @version  5.0
    108 * @author   Kazuhiko Hasegawa
    109 * @since    JDK6.0,
    110 */
    111public final class DisplayCapture implements FlavorListener {
    112        private static final Clipboard CLIP_BOARD = Toolkit.getDefaultToolkit().getSystemClipboard();
    113        private static final Rectangle SCRN_SIZE  = new Rectangle( Toolkit.getDefaultToolkit().getScreenSize() );
    114
    115        private File    baseDir         = new File(".");                // セーブするベースディレクトリ
    116        private String  firstID         = "seq";                                // 保存時のファイル名の形式(seq|gui)
    117        private String  imgType         = "png" ;                               // 画像形式(png|gif|jpg)
    118        private int             cnt                     = 100;                                  // ユニーク番号(セーブファイル名に付与)
    119
    120        /**
    121         * キャプチャファイルをセーブするベースとなるディレクトリを設定します(初期値:java実行フォルダ)。
    122         *
    123         * クラスの内部的には、Java の実行フォルダ( new File(".") ) が初期値です。
    124         *
    125         * @param       bsDir   セーブするベースディレクトリ
    126         * @throws RuntimeException セーブフォルダが作成できなかった場合
    127         */
    128        public void setBaseDir( final String bsDir ) {
    129                if( bsDir != null && bsDir.length() > 0 ) {
    130                        baseDir = new File( bsDir );
    131
    132                        // ベースフォルダの作成。初期値は起動フォルダなので、必ず存在する(はず)
    133                        if( !baseDir.exists() && !baseDir.mkdirs() ) {
    134                                final String errMsg = "ERROR:セーブフォルダが作成できませんでした。" + baseDir.getAbsolutePath()  ;
    135                                throw new OgRuntimeException( errMsg );
    136                        }
    137                }
    138        }
    139
    140        /**
    141         * キャプチャ画像をセーブするファイル方式を指定します(初期値:seq)。
    142         *
    143         *    seq (連番_画面ID_JSPファイル名.画像形式)
    144         *    gui (画面ID_JSPファイル名_連番.画像形式)
    145         *
    146         * 初期値は、seq です。
    147         *
    148         * @param       firstID セーブするファイル方式(seq|gui)
    149         * @throws RuntimeException ファイル方式の指定が間違っていた場合
    150         */
    151        public void setFirstID( final String firstID ) {
    152                if( firstID != null ) {
    153                        if( firstID.matches( "seq|gui" ) ) {
    154                                this.firstID = firstID;
    155                        }
    156                        else {
    157                                final String errMsg = "ERROR:firstID 属性は、(seq|gui)でお願いします。firstID=[" + firstID + "]" ;
    158                                throw new OgRuntimeException( errMsg );
    159                        }
    160                }
    161        }
    162
    163        /**
    164         * キャプチャ画像をセーブする画像形式を指定します(初期値:png)。
    165         *
    166         * キャプチャされたイメージをセーブするときの画像形式を指定できます。
    167         * ここでは、png , gif ,jpg を指定できます。
    168         *
    169         * 初期値は、png 形式です。
    170         *
    171         * @param       imgType セーブする画像形式(png|gif|jpg)
    172         */
    173        public void setImageType( final String imgType ) {
    174                if( imgType != null && imgType.matches( "png|gif|jpg" ) ) {
    175                        this.imgType = imgType;
    176                }
    177        }
    178
    179        /**
    180         * キャプチャ画像をセーブするファイル名の先頭に付ける連番の開始数(初期値:100)。
    181         *
    182         * キャプチャされたイメージをセーブするとき、画面IDとJSPファイル名だけでは、前回分を
    183         * 上書きしてしまうため、ファイル名の先頭に連番を付与しています。
    184         * ここでは、その連番の開始番号を指定できます。
    185         *
    186         * 初期値は、100 です。
    187         *
    188         * @param       startCnt        連番の開始数(初期値:100)
    189         */
    190        public void setStartCnt( final String startCnt ) {
    191                if( startCnt != null && startCnt.length() > 0 ) {
    192                        cnt = Integer.parseInt( startCnt );
    193                }
    194        }
    195
    196        /**
    197         * 全画面の画像イメージ(キャプチャ画像)を取得します。
    198         *
    199         * java.awt.Toolkit で、全画面のスクリーンサイズを取得し、java.awt.Robot の
    200         * createScreenCapture( Rectangle ) メソッドで、BufferedImage を取得しています。
    201         *
    202         * @return      全画面の画像イメージ
    203         * @throws RuntimeException AWTException が発生した場合
    204         */
    205        public static BufferedImage doCapture() {
    206                BufferedImage img = null;
    207                try{
    208                        final Robot robo = new Robot();
    209                        img = robo.createScreenCapture( SCRN_SIZE );
    210                }
    211                catch( final AWTException ex ) {
    212                        final String errMsg = "ERROR:画像イメージ(キャプチャ画像)が取得できませんでした。" ;
    213                        setClipboard( errMsg );
    214                        throw new OgRuntimeException( errMsg,ex );
    215                }
    216                catch( final Throwable th ) {
    217                        final String errMsg = "ERROR:" + th.getLocalizedMessage() ;
    218                        setClipboard( errMsg );
    219                        throw new OgRuntimeException( errMsg,th );
    220                }
    221
    222                return img;
    223        }
    224
    225        /**
    226         * キャプチャ画像をファイルにセーブします。
    227         *
    228         * ここでは、単純に、引数そのままで、ImageIO.write( BufferedImage,String,File ) しています。
    229         * saveFile のディレクトリ存在チェックや、ファイル名の拡張子(png,gif,jpgなど)の修正、
    230         * imgType の形式チェックなどは、行っていません。
    231         * それらの処理は、事前に、調整しておいてください。
    232         *
    233         * @param       img     セーブする画像イメージ
    234         * @param       imgType セーブする画像形式(png|gif|jpg)
    235         * @param       saveFile        セーブする画像ファイルオブジェクト
    236         * @throws RuntimeException IOException が発生した場合
    237         * @see  javax.imageio.ImageIO#write( java.awt.image.RenderedImage , String , java.io.File )
    238         */
    239        public static void saveImage( final BufferedImage img , final String imgType , final File saveFile ) {
    240                try{
    241                        ImageIO.write( img,imgType,saveFile );
    242                }
    243                catch( final IOException ex ) {
    244                        final String errMsg = "ERROR:キャプチャ画像をファイルにセーブできませんでした。" + saveFile.getAbsolutePath()  ;
    245                        setClipboard( errMsg );
    246                        throw new OgRuntimeException( errMsg,ex );
    247                }
    248                catch( final Throwable th ) {
    249                        final String errMsg = "ERROR:" + th.getLocalizedMessage() ;
    250                        setClipboard( errMsg );
    251                        throw new OgRuntimeException( errMsg,th );
    252                }
    253        }
    254
    255        /**
    256         * システムのクリップボードの文字列を取得します。
    257         *
    258         * Toolkit.getDefaultToolkit().getSystemClipboard() で取得された  Clipboard オブジェクトから
    259         * 文字列情報(DataFlavor.stringFlavor)を取得します。
    260         * 文字列情報が取得できない場合、(UnsupportedFlavorException が発生した場合) 例えば、
    261         * PrntScrn ボタンが押された場合などは、文字列として、"GUI:PRINT SCREEN.img" を返します。
    262         * これは、文字列が返せない場合でも、クリップボードに書き込まれたイベントで、全画面のキャプチャを
    263         * 取得するための、特殊なコマンドに相当します。
    264         *
    265         * @return      クリップボードの文字列
    266         * @throws RuntimeException IOException が発生した場合
    267         * @see  java.awt.datatransfer.Clipboard#getData( DataFlavor )
    268         */
    269        public static String getClipboard() {
    270                String strClip = null;
    271
    272                // 方法として、Transferable を取得後、getTransferData する事もできる。
    273        //      Transferable data = CLIP_BOARD.getContents(null);
    274
    275                try {
    276                        // クリップボードの値を取得
    277        //              strClip = (String)data.getTransferData(DataFlavor.stringFlavor);
    278                        strClip = (String)CLIP_BOARD.getData( DataFlavor.stringFlavor );
    279                }
    280                catch( final UnsupportedFlavorException ex ) {
    281                        // PrintScreen が押された場合。
    282                        strClip = "GUI:PRINT SCREEN.img" ;              // 形式をGUI:画面ID xxxxx.jsp 形式に合わす為
    283                }
    284                catch( final IOException ex ) {
    285                        final String errMsg = "ERROR:クリップボードの値を取得できませんでした。" ;
    286                        setClipboard( errMsg );
    287                        throw new OgRuntimeException( errMsg,ex );
    288                }
    289                catch( final Throwable th ) {
    290                        final String errMsg = "ERROR:" + th.getLocalizedMessage() ;
    291                        setClipboard( errMsg );
    292                        throw new OgRuntimeException( errMsg,th );
    293                }
    294
    295                return strClip;
    296        }
    297
    298        /**
    299         * システムのクリップボードに文字列を書き込みます。
    300         *
    301         * システムの Clipboard オブジェクトに、StringSelection を セットします。
    302         * 通常であれば、単純に、クリップボード経由でデータのやり取りをするだけの機構ですが、
    303         * FlavorListener を実装している関係上、flavorsChanged が発生します。
    304         * このイベントについては、#flavorsChanged( FlavorEvent ) を参照ください。
    305         *
    306         * @param       txt     クリップボードに書き込む文字列
    307         * @see  java.awt.datatransfer.StringSelection
    308         * @see  java.awt.datatransfer.Clipboard#setContents( Transferable , ClipboardOwner )
    309         */
    310        public static void setClipboard( final String txt ) {
    311                final StringSelection strSel = new StringSelection( txt );
    312                CLIP_BOARD.setContents( strSel, null );
    313        }
    314
    315        /**
    316         * リスナー対象の Clipboard で使用可能な DataFlavor が変更されたときに呼び出されます。
    317         *
    318         * これは、FlavorListener の イベントの実装です。
    319         * DataFlavor が変更されたときであり、そのデータの内容が書き換えられた場合には、イベントが
    320         * 発生しません。
    321         * そのため、データを取り出したあとで、Transferable を再セットする処理を行っています。
    322         *
    323         * クリップボードで使用可能な一連の DataFlavors の変更によるものでない、余分な通知もあります。
    324         * さらに、イベントを発生させるために、Transferable をセットする処理( #setClipboard(String) )を
    325         * 実行しても、同様にイベントが発生します。
    326         *
    327         * ここでは、取得したクリップボードの文字列が、"GUI:" の場合のみ処理しています。
    328         * これにより、取得後の Transferable の再セット時の文字列は、"GUI:" を削除しています。
    329         *
    330         * このメソッドでは、画面キャプチャを取得し、クリップボードの文字列から、画面ID とJSPファイル名を
    331         * 抜き出し、セーブする一連の処理を行っています。
    332         *
    333         * @param       fe      イベントソース
    334         * @see  java.awt.datatransfer.FlavorListener#flavorsChanged( FlavorEvent )
    335         */
    336        @Override
    337        public void flavorsChanged( final FlavorEvent fe )  {
    338                final String txt = getClipboard();
    339
    340                // クリップボードの値をクリアしたときのイベントは、拾わないため。
    341                if( txt != null && txt.length() > 0 && txt.startsWith( "GUI:" ) ) {
    342                        System.out.println( cnt + ":【" + txt + "】" );
    343                        final BufferedImage img = doCapture();
    344
    345                        final File saveFile = makeSaveFile( txt );
    346
    347                        saveImage( img,imgType,saveFile );
    348
    349                        // クリップボードのFlavorを置換します。(Windwosからセットされた時にイベントを発生させるため。)
    350                        // 先頭の GUI: を取り除く。イベントの無限ループを防ぐ意味。
    351                        setClipboard( txt.substring( 4 ) );
    352                }
    353        }
    354
    355        /**
    356         * キャプチャ画像を書き出すファイルオブジェクトを作成します。
    357         *
    358         * 引数は、"GUI:画面ID xxxxx.jsp" 形式を想定した文字列です。
    359         *
    360         * ファイル名には、２種類あります。
    361         *   firstID を seq に設定した場合
    362         *      ベースフォルダでフォルダを作成し、その中に、連番_画面ID_JSPファイル名.画像形式 ファイルを作成します。
    363         *      ファイルは、キャプチャされた順番に、画面IDも混在して作成されます。つまり、ファイル名の順番に
    364         *      再生すれば、リンクや他のシステムとの連携などで、画面が行き来しても、作業の順番にキャプチャできて
    365         *      いる事になります。
    366         *   firstID を gui に設定した場合
    367         *      ベースフォルダでフォルダを作成し、その中に、画面ID_JSPファイル名_連番.画像形式 ファイルを作成します。
    368         *      画面ID 単位に、画面のキャプチャを整理、使用したい場合に便利です。
    369         *      ファイルのタイムスタンプ(作成時刻)で並び替えを行えば、キャプチャ順に並び替えできます。
    370         *
    371         * このメソッドで、フォルダの存在チェック、および、無ければ作成(mkdirs)も行います。
    372         *
    373         * @param       txt     ファイル名の元となる文字列("GUI:画面ID xxxxx.jsp" 形式)
    374         *
    375         * @return      書き出すファイルオブジェクト
    376         */
    377        private File makeSaveFile( final String txt ) {
    378                final int spc = txt.indexOf( ' ' );                                                     // "GUI:画面ID xxxxx.jsp" をスペースで分離
    379                final String gui = txt.substring( 4,spc );                                      //      画面ID の部分のみ切り出す。
    380                final String jsp = txt.substring( spc+1,txt.length()-4 );               //             xxxxx の部分のみ切り出す。
    381
    382                String saveFile = null;
    383                if( "seq".equalsIgnoreCase( firstID ) ) {
    384                        saveFile = cnt++ + "_" + gui + "_" + jsp + "." + imgType ;
    385                }
    386                else if( "gui".equalsIgnoreCase( firstID ) ) {
    387                        saveFile = gui + "_" + jsp + "_" + cnt++ + "." + imgType ;
    388                }
    389                else {          // 5.5.2.6 (2012/05/25) findbugs対応
    390                        saveFile = cnt++ + "_" + gui + "_" + jsp + "." + imgType ;              // seqかguiしかないが、経路として、初期値(seq)を設定しておく。
    391                }
    392
    393                final File svf = new File( baseDir,saveFile );
    394                // セーブフォルダの作成。
    395                final File parent = svf.getParentFile();
    396                if( !parent.exists() && !parent.mkdirs() ) {
    397                        final String errMsg = "ERROR:セーブフォルダが作成できませんでした。" + parent.getAbsolutePath()  ;
    398                        setClipboard( errMsg );
    399                        throw new OgRuntimeException( errMsg );
    400                }
    401
    402                // セーブファイルの存在チェック。上書き禁止にしておきます。
    403                if( svf.exists() ) {
    404                        final String errMsg = "ERROR:セーブファイルがすでに作成されています。" + svf.getAbsolutePath()  ;
    405                        setClipboard( errMsg );
    406                        throw new OgRuntimeException( errMsg );
    407                }
    408
    409                return svf;
    410        }
    411
    412        /**
    413         * DisplayCapture.java は、画面イメージをキャプチャする、メインメソッドです。
    414         *
    415         * Javaアプリケーションとして実行すると、無限処理に入ります。
    416         * 内部的には、flavorsChanged イベント によるクリップボードの監視を行います。
    417         * クリップボードに、"GUI:画面ID xxxxx.jsp" 形式の文字が書き込まれると、画面キャプチャを、
    418         * ファイルに書き出す処理が実行されます。
    419         * 書き出すファイル名の初期形式は、firstID の設定により異なります。
    420         *   firstID を seq に設定した場合
    421         *      ベースフォルダでフォルダを作成し、その中に、連番_画面ID_JSPファイル名.画像形式 ファイルを作成します。
    422         *      ファイルは、キャプチャされた順番に、画面IDも混在して作成されます。つまり、ファイル名の順番に
    423         *      再生すれば、リンクや他のシステムとの連携などで、画面が行き来しても、作業の順番にキャプチャできて
    424         *      いる事になります。
    425         *   firstID を gui に設定した場合
    426         *      ベースフォルダでフォルダを作成し、その中に、画面ID_JSPファイル名_連番.画像形式 ファイルを作成します。
    427         *      画面ID 単位に、画面のキャプチャを整理、使用したい場合に便利です。
    428         *      ファイルのタイムスタンプ(作成時刻)で並び替えを行えば、キャプチャ順に並び替えできます。
    429         *
    430         * Usage: java org.opengion.fukurou.util.DisplayCapture
    431         *                   [BASE_DIR] [firstID(seq|gui)] [imageFormat(png|gif|jpg)] [startCnt(100)]
    432         *
    433         *     args[0]  BASE_DIR    : キャプチャファイルをセーブするベースとなるディレクトリ(初期値:起動フォルダ)
    434         *     args[1]  firstID     : キャプチャ画像をセーブするファイル方式を指定します(初期値:seq)
    435         *                            seq (連番_画面ID_JSPファイル名.画像形式)
    436         *                            gui (画面ID_JSPファイル名_連番.画像形式)
    437         *     args[2]  imageFormat : 作成するイメージの形式。png|gif|jpg のどれか(初期値:png)
    438         *     args[3]  startCnt    : セーブファイル名をユニークにするためのカウント(初期値:100)
    439         *
    440         * @param       args    引数 [BASE_DIR] [firstID(seq|gui)] [imageFormat(png|gif|jpg)] [startCnt(100)]
    441         */
    442        public static void main( final String[] args ) {
    443                System.out.println( "DisplayCapture を起動しました。" );
    444
    445                final DisplayCapture dispCap = new DisplayCapture();
    446
    447                if( args.length > 0 ) { dispCap.setBaseDir(   args[0] ); }
    448                if( args.length > 1 ) { dispCap.setFirstID(   args[1] ); }
    449                if( args.length > 2 ) { dispCap.setImageType( args[2] ); }
    450                if( args.length > 3 ) { dispCap.setStartCnt(  args[3] ); }
    451
    452                // クリップボードの値をクリア(FlavorEvent を起こさせるため)
    453                DisplayCapture.setClipboard( null );
    454
    455                // FlavorListener を登録します。(自分自身のオブジェクト)
    456                CLIP_BOARD.addFlavorListener( dispCap );
    457
    458                // FlavorEvent で処理させるので、ずっとスレッドをSleepさせておけばよい。
    459                while( true ) {
    460                        try {
    461                                Thread.sleep( 100000 );
    462                        }
    463                        catch( final InterruptedException ex ) {}
    464                }
    465        }
    466}