/* =============================================================================== VideoCapt.mc : 静止画または動画のキャプチャー =============================================================================== Copyright (C) 2011-2013 Masahiko Watanabe Edition History: 2011.10.17 初版( GK, MK Library: Rev.2011.10.17 使用 ) 2013.02.15 MikoScript3(ユニコード版)用に変更 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ≪ 説明 ≫ ■静止画 ・取り込み対象領域 ・デスクトップ上の任意の矩形範囲 ・カメラ入力 ・取り込み画像の拡大/縮小可 ・保存先(取り込み後に指定) ・任意のファイル(現状、格納形式は BMP のみ) ・クリップボード ・ホットキー ・Alt+Ctl+S 取り込み実行 ■動画 ・録画対象領域 ・デスクトップ上の任意の矩形範囲 ・カメラ入力 ・サンプリング周期 ・10 回/秒( 1〜10 回の範囲で調整可 ) ・最大録画時間: 現状 10 分に限定 ・保存先(録画開始前に指定) ・任意のファイル(各種の画像圧縮形式を選択可) ・ホットキー ・Alt+Ctl+M 録画開始 ・Alt+Ctl+E 録画終了 ■特記事項 ・本ウィンドウには、メニューバーがない。メニューを表示させるには、 タイトルバー上を右クリックするか、または、 タイトルバー左端のアイコンを左クリックする。 ・本ウィンドウのクライアント領域が透明の時、その領域内に見えている デスクトップ上の画像が、取り込みの対象になる。 ・本クライアント領域の位置とサイズは、手動変更だけでなく、 その直下のウィンドウやクライアント領域に合うように自動変更可。 ・本クライアント領域の位置とサイズの微調整は、以下のキーで操作可。 ・矢印キー : その方向にウィンドウ全体を平行移動 ・Shift+矢印キー: 右端/下端をその方向に移動(左端/上端は固定) ・Ctrl+矢印キー : 左端/上端をその方向に移動(右端/下端は固定) ・マウスカーソルの画像は、デスクトップ上の矩形範囲の取り込みの場合、 マウスカーソルがその範囲内にあれば含まれる。 カメラ入力の場合は含まれない。 ・静止画が取り込まれると、本クライアント領域に表示される。 取り込み画像の拡大/縮小は、本ウィンドウのサイズ変更に随伴。 ・動画の保存ファイル名は、録画開始前に指定されていないと、 本スクリプトがあるフォルダー内の "temp.avi" というファイルになる。 ・画像圧縮形式は、OSがサポートするもののみ選択可。 高圧縮率の形式は、処理時間が、録画周期に間に合わない場合がある。 低圧縮率の形式は、処理は速いが、記録容量が膨大になる。 ・カメラ入力の確認は、Logicool Webcam C310 を使用(それ以外は未確認) -----------------------------------------------------------------------------*/ #include #include #include #include if( ::Module.Include( "GkLibrary" ) > 0 && ::Module.Include( "MkLibrary" ) > 0 ) Main( argc, argv ); return; //----------------------------------------------------------------------------- // デバッグプリント無効化(リリース時) #set pc #comment #set pv // デバッグプリント有効化(デバッグ時) //#set pc print //#set pv print #set p print //----------------------------------------------------------------------------- // マクロ定義 #set MAIN_TITLE "画像キャプチャー" // ウィンドウのキャプション #set WinCXd 400 // ウィンドウのクライアント領域の初期幅 #set WinCYd 300 // ウィンドウのクライアント領域の初期高 #set MIN_SAMPLE_RATE 1 // 動画の最小サンプリング周期(回/秒) #set MAX_SAMPLE_RATE 10 // 動画の最大サンプリング周期(回/秒) #set DEF_SAMPLE_RATE 10 // 動画の初期サンプリング周期(回/秒) #set MAX_RECORD_TIME 10*60 // 動画の最大録画時間(秒) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #set TM_CAPTURE 101 // 静止画取り込み開始管理タイマー #set TM_RECORD 102 // 動画の録画開始管理タイマー #set TM_REC_FRAME 103 // 動画1フレームの記録タイマー #set CMD_CAP_STILL_IMAGE 201 // 静止画取り込み(即時) #set CMD_REC_MOVIE_START 202 // 動画の録画開始(即時) #set CMD_REC_MOVIE_END 203 // 動画の録画終了 #set ST_TRANS_VIEW 0 // 〜 透明窓 〜 #set ST_CAMERA_VIEW 1 // 〜 カメラ入力表示中 〜 #set ST_CAP_IMAGE_VIEW 2 // 〜 静止画取り込み完了 〜 #set ST_REC_MOVIE_ON 3 // 〜 録画中( .. 秒 )〜 #set ST_REC_MOVIE_DONE 4 // 〜 録画終了( .. 秒 )〜 #set ST_REC_MOVIE_ERROR 5 // 〜 録画エラー 〜 #set ST_1SEC_BEFORE 11 // 〜 1秒前 〜 #set ST_2SEC_BEFORE 12 // 〜 2秒前 〜 #set ST_3SEC_BEFORE 13 // 〜 3秒前 〜 //============================================================================= function Main( argc, argv ) { #pc "開始"; ^DefValue = null; ^Initialize(); R = ::GK.Rect( 0, 0, #WinCXd, #WinCYd ); ::GK.Window.GetAdjRect( R, #FALSE, #WS_CAPTION | #WS_SYSMENU | #WS_THICKFRAME, #WS_EX_TOPMOST ); R.Xo = ( ::GK.GetSystemMetrics( #SM_CXSCREEN ) - R.Xd ) / 2; R.Yo = ( ::GK.GetSystemMetrics( #SM_CYSCREEN ) - R.Yd ) / 2; ^MainWindow.Construct( #MAIN_TITLE, R.Xo, R.Yo, R.Xd, R.Yd, #WS_CAPTION | #WS_SYSMENU | #WS_THICKFRAME, #WS_EX_TOPMOST ); //| #WS_EX_LAYERED ); ^MainWindow.Open(); ::GK.WindowMsgLoop(); ^Terminate(); #pc "終了"; } //----------------------------------------------------------------------------- function ^Initialize() { class ^MainWindow : ::GK.Window {} class ^DlgWnd_ClientSize : ::GK.DlgWindow {} class ^DlgWnd_SampleRate : ::GK.DlgWindow {} ^DlgFont = ::GK.Font( "MS ゴシック", 14, #SHIFTJIS_CHARSET, #FIXED_PITCH ); #pv ^GetCursorInfo = ::GK.User32.GetEntry( "GetCursorInfo" ); #pv ^GetIconInfo = ::GK.User32.GetEntry( "GetIconInfo" ); #pv ^DrawIcon_ = ::GK.User32.GetEntry( "DrawIcon" ); ^CURSORINFO ::= { .cbSize 'ULONG; .flags 'ULONG; .hCursor 'ULONG; .ptScreenPos_x 'LONG; .ptScreenPos_y 'LONG; } ^CursInfoSize'ULONG = ^CURSORINFO'size; ^ICONINFO ::= { .fIcon 'ULONG; .xHotspot 'ULONG; .yHotspot 'ULONG; .hbmMask 'ULONG; .hbmColor 'ULONG; } sz = ^CursInfoSize + ^ICONINFO'size; ^CursIconInfo = ::Buffer( sz ); ^CursIconInfo.Keep( sz ); } function ^Terminate() { delete ^DlgWnd_SampleRate; delete ^DlgWnd_ClientSize; delete ^MainWindow; delete ^DlgFont; } //============================================================================= function ^MainWindow.OnCreate() { #pc "OnCreate"; //.SetLayerAttr( 0, 128, #LWA_ALPHA ); .SetupMenus(); .SetExtWndProc( #WM_HOTKEY , .OnHotKey , this ); #pv RegisterHotKey = ::GK.User32.GetEntry( "RegisterHotKey" ); RegisterHotKey( .hWnd, #CMD_CAP_STILL_IMAGE, #MOD_ALT | #MOD_CONTROL, `S` ); RegisterHotKey( .hWnd, #CMD_REC_MOVIE_START, #MOD_ALT | #MOD_CONTROL, `M` ); RegisterHotKey( .hWnd, #CMD_REC_MOVIE_END , #MOD_ALT | #MOD_CONTROL, `E` ); .CapFileName = "temp.bmp"; // 画像格納ファイル名称の初期値 .CapFilePath = ::Module.FilePath(); // 画像格納ファイルパスの初期値 .AviFileName = "temp.avi"; // 動画格納ファイル名称の初期値 .AviFilePath = ::Module.FilePath() + "\\"; // 動画格納ファイルパスの初期値 .SampleRate = #DEF_SAMPLE_RATE; // 動画のサンプル周期(回/秒) //.gc.SetTextColor( #RGB(0,0,0) ); .gc.SetBackMode( #TRANSPARENT ); .gc.SetFont( ^DlgFont ); .ShowCapStatus( #ST_TRANS_VIEW ); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.OnClose() { #pc "OnClose"; if( ^DlgWindow.hWnd != null ) ^DlgWindow.Close(); #pv UnregisterHotKey = ::GK.User32.GetEntry( "UnregisterHotKey" ); UnregisterHotKey( .hWnd, #CMD_CAP_STILL_IMAGE ); UnregisterHotKey( .hWnd, #CMD_REC_MOVIE_START ); UnregisterHotKey( .hWnd, #CMD_REC_MOVIE_END ); if( .CameraView ) .CameraView.Preview( #FALSE ); .CloseAviFileStream(); .FreeCurCompressor(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.OnSize() { //#pc "OnSize", .gc.Xd, .gc.Yd; if( .CameraView ) .CameraView.Move( ::GK.Rect( 0, 0, .gc.Xd, .gc.Yd ) ); else if( .CapFrame ) ; // OnPaint() で描画 else .SetWndRgnForCap(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.OnPaint() { //#pc "OnPaint: " : .gc.Xd, .gc.Yd; if( .CapFrame ) .gc.CopyImage( .CapFrame, ::GK.Rect( 0, 0, .gc.Xd, .gc.Yd ), , #SRCCOPY ); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.SetupMenus() { .sys_menu = ::GK.SysMenu( this ); // .sys_menu.DeleteItem( #SC_SIZE ); ← この削除を行なうとウィンドウのサイズ変更ができなくなる! // .sys_menu.DeleteItem( #SC_MOVE ); ← この削除を行なうとウィンドウの移動ができなくなる! .sys_menu.DeleteItem( #SC_RESTORE ); .sys_menu.DeleteItem( #SC_MINIMIZE ); .sys_menu.DeleteItem( #SC_MAXIMIZE ); .menu_1 = ::GK.Menu(); // 表示 .menu_1.InsertItem( 11, "直下のウィンドウ全体" ); .menu_1.InsertItem( 12, "直下のクライアント領域" ); .menu_1.InsertItem( 13, "表示サイズ指定..." ); .menu_1.InsertItem( 14, "カメラ入力" ); .menu_1.InsertItem( 15, "カメラ設定..." ); .menu_1.InsertItem( 16, "本来の表示サイズに戻す" ); .menu_1.InsertItem( 17, "透明化" ); .menu_2 = ::GK.Menu(); // 静止画 .menu_2.InsertItem( 21, "取り込み開始( 即時 )\tAlt+Ctl+S" ); .menu_2.InsertItem( 22, "取り込み開始(3秒後)" ); .menu_2.InsertItem( 23, "ファイルへ保存..." ); .menu_2.InsertItem( 24, "クリップボードへ保存" ); .menu_3 = ::GK.Menu(); // 動画 .menu_3.InsertItem( 31, "録画開始( 即時 )\tAlt+Ctl+M" ); .menu_3.InsertItem( 32, "録画開始(3秒後)" ); .menu_3.InsertItem( 33, "録画終了\tAlt+Ctl+E" ); .menu_3.InsertItem( 34, "圧縮方式..." ); .menu_3.InsertItem( 35, "サンプリング周期..." ); .menu_3.InsertItem( 36, "保存ファイル名..." ); .sys_menu.InsertSeparator( 0 ); .sys_menu.InsertSeparator( 0 ); .sys_menu.InsertMenu( .menu_3, "■動画", 0 ); .sys_menu.InsertMenu( .menu_2, "■静止画", 0 ); .sys_menu.InsertMenu( .menu_1, "■表示", 0 ); } function ^MainWindow.OnSysMenu( id ) { #pc "OnSysMenu: ": id; switch( id ) { // 表示 case 11: .AdjustWnd( #TRUE ); break; // 直下のウィンドウ全体 case 12: .AdjustWnd( #FALSE ); break; // 直下のクライアント領域 case 13: ^DlgWnd_ClientSize.Display( // 表示サイズ指定... "表示サイズ", this, 30, 40, 250, 220 ); break; case 14: .ShowCameraView(); break; // カメラ入力 case 15: .DlgCameraFormat(); break; // カメラ設定... case 16: .ResumeOrgViewSize(); break; // 本来の表示サイズに戻す case 17: .SetTransparentView(); break; // 透明化 // 静止画 case 21: // 取り込み( 即時 ) if( ! .CanCapStart?() ) break; .Delay = 0; .StartTimer( #TM_CAPTURE, 500 ); //(注)このタイマーはメニュー表示が取り込まれないようにするため break; case 22: // 取り込み(3秒後) if( ! .CanCapStart?() ) break; .Delay = 3; .StartTimer( #TM_CAPTURE, 50 ); break; case 23: .SaveCapToFile(); break; // ファイルへ保存... case 24: .SaveCapToClip(); break; // クリップボードへ保存 // 動画 case 31: // 録画開始( 即時 ) if( ! .CanRecStart?() ) break; .Delay = 0; .StartTimer( #TM_RECORD, 500 ); //(注)このタイマーはメニュー表示が取り込まれないようにするため break; case 32: // 録画開始(3秒後) if( ! .CanRecStart?() ) break; .Delay = 3; .StartTimer( #TM_RECORD, 50 ); break; case 33: .StopRecordMovie(); break; // 録画終了 case 34: .DlgChooseCompressor(); break; // 圧縮方式... case 35: ^DlgWnd_SampleRate.Display( // サンプリング周期 "サンプリング周期", this, 20, 60, 280, 120 ); break; case 36: .DlgChooseFileToSave(); break; // 保存ファイル名... default: return #FALSE; //warp DEF_WINDOW_PROC; } return #TRUE; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.OnHotKey( wprm, lprm ) { #pc "OnHotKey: ": wprm, lprm'x; switch( wprm ) { case #CMD_CAP_STILL_IMAGE: .CaptureCurView(); break; case #CMD_REC_MOVIE_START: .StartRecordMovie(); break; case #CMD_REC_MOVIE_END : .StopRecordMovie(); break; } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.OnKeyDown( vk ) { //#pc "OnKeyDown: ": vk'x(4); switch( vk & ~#KCF_REPEAT ) { case #VK_ESCAPE: .Close(); break; case #VK_RETURN: .CaptureCurView(); break; case #VK_LEFT: .ShiftWnd( -1, 0 ); break; case #VK_UP: .ShiftWnd( 0, -1 ); break; case #VK_RIGHT: .ShiftWnd( +1, 0 ); break; case #VK_DOWN: .ShiftWnd( 0, +1 ); break; case #VK_LEFT +#KCF_SHIFT: .RB_ResizeWnd( -1, 0 ); break; case #VK_UP +#KCF_SHIFT: .RB_ResizeWnd( 0, -1 ); break; case #VK_RIGHT+#KCF_SHIFT: .RB_ResizeWnd( +1, 0 ); break; case #VK_DOWN +#KCF_SHIFT: .RB_ResizeWnd( 0, +1 ); break; case #VK_LEFT +#KCF_CTRL: .LT_ResizeWnd( +1, 0 ); break; case #VK_UP +#KCF_CTRL: .LT_ResizeWnd( 0, +1 ); break; case #VK_RIGHT+#KCF_CTRL: .LT_ResizeWnd( -1, 0 ); break; case #VK_DOWN +#KCF_CTRL: .LT_ResizeWnd( 0, -1 ); break; } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.OnTimer( id ) { //#pc "OnTimer: ", id; switch( id ) { case #TM_CAPTURE: break; case #TM_RECORD: break; case #TM_REC_FRAME: .RecordMovieFrame(); default: return; } if( .Delay > 0 ) { .Delay--; .ShowCapStatus( #ST_1SEC_BEFORE + .Delay ); .StartTimer( id, 1000 ); return; } .StopTimer( id ); .ShowCapStatus(); if( id == #TM_CAPTURE ) .CaptureCurView(); else .StartRecordMovie(); } //----------------------------------------------------------------------------- function ^MainWindow.ShowCapStatus( s ) { switch( s ) { case #ST_TRANS_VIEW: t = "〜 透明窓 〜"; break; case #ST_CAMERA_VIEW: t = "〜 カメラ入力表示中 〜"; break; case #ST_CAP_IMAGE_VIEW: t = "〜 静止画取り込み完了 〜"; break; case #ST_REC_MOVIE_ON: t = "〜 録画中( " + .RecSec'd + " 秒 )〜"; break; case #ST_REC_MOVIE_DONE: t = "〜 録画終了( " + .RecSec'd + " 秒 )〜"; break; case #ST_REC_MOVIE_ERROR: t = "〜 録画エラー 〜"; break; case #ST_1SEC_BEFORE: t = "〜 1秒前 〜"; break; case #ST_2SEC_BEFORE: t = "〜 2秒前 〜"; break; case #ST_3SEC_BEFORE: t = "〜 3秒前 〜"; break; default: t = ""; break; } .SetTitle( #MAIN_TITLE " " + t ); } //----------------------------------------------------------------------------- function ^MainWindow.CaptureCurView() // 現クライアント領域内の画像を取り込む { #pc "CaptureCurView"; if( ! .CanCapStart?() ) return; // デスクトップ内の本クライアント領域内の画像を取り込む R = ::GK.Rect( 0, 0, .gc.Xd, .gc.Yd ); ( R.Xo, R.Yo ) = .ClientToScreen( 0, 0 ); WndView = ::GK.WndView(); // デスクトップの画面 .CapFrame = ::GK.VFrame( .gc.Xd, .gc.Yd, 24 ); .CapFrame.CopyImage( WndView, , R, #SRCCOPY ); if( .CameraView ) // カメラ入力画像表示中? { .CameraView.Preview( #FALSE ); .CameraView.Destroy(); delete .CameraView; } else .CapFrame.[ ^DrawCursor ]( R.Xo, R.Yo ); // マウスカーソルを描画 .SetWindowRgn( #NULL, #FALSE ); .gc.CopyImage( .CapFrame, , , #SRCCOPY ); .ShowCapStatus( #ST_CAP_IMAGE_VIEW ); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.CanCapStart?() { if( .AviFile ) // 録画中? return #FALSE; if( .CapFrame ) { .MessageBox( "取り込みは、" "透明窓かカメラ入力画面の時に実行できます。", #MAIN_TITLE, #MB_ICONINFORMATION | #MB_OK ); return #FALSE; } return #TRUE; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // 取り込み画像またはカメラ入力画像を消して、クライアント領域を透明にする function ^MainWindow.SetTransparentView() { if( .CapFrame ) { .SetWndRgnForCap(); delete .CapFrame; } else if( .AviFile ) // 録画中? return; if( .CameraView ) { .SetWndRgnForCap(); .CameraView.Preview( #FALSE ); .CameraView.Destroy(); delete .CameraView; } .ShowCapStatus( #ST_TRANS_VIEW ); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ウィンドウリージョンを設定して、クライアント領域を透明化 function ^MainWindow.SetWndRgnForCap() { R = ::GK.Rect(); .GetWindowRect( R ); ( Xo, Yo ) = .ClientToScreen( 0, 0 ); Xo -= R.Xo; Yo -= R.Yo; R.Xo = 0; R.Yo = 0; Rgn = ::GK.Region( R ); Rgn -= ::GK.Region( ::GK.Rect( Xo, Yo, .gc.Xd, .gc.Yd ) ); .SetWindowRgn( Rgn, #TRUE ); } //----------------------------------------------------------------------------- function ^MainWindow.ShiftWnd( dx, dy ) // 本ウィンドウを平行移動 { .GetWindowRect( R'cbox! ); R.Xo += dx; R.Yo += dy; .Move( R ); } function ^MainWindow.RB_ResizeWnd( dx, dy ) // 本ウィンドウの右下隅位置を移動 { .GetWindowRect( R'cbox! ); R.Xd += dx; R.Yd += dy; .Move( R ); } function ^MainWindow.LT_ResizeWnd( dx, dy ) // 本ウィンドウの左上隅位置を移動 { .GetWindowRect( R'cbox! ); R.Xo -= dx; R.Yo -= dy; R.Xd += dx; R.Yd += dy; .Move( R ); } function ^MainWindow.ResumeOrgViewSize() // 本来の画像表示サイズに戻す { if( .CapFrame ) { .GetWindowRect( R'cbox! ); R.Xd += .CapFrame.Xd - .gc.Xd; R.Yd += .CapFrame.Yd - .gc.Yd; .Move( R ); } else if( .AviFile ) // 録画中? ; else if( .CameraView ) .AdjWndSizeToCamera( #FALSE ); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // 本ウィンドウのクライアント領域を、取り込み対象ウィンドウの領域へ移動させる function ^MainWindow.AdjustWnd( full? ) { .SetTransparentView(); // 対象ウィンドウの検索 #pv ( Xc, Yc ) = .ClientToScreen( .gc.Xd / 2, .gc.Yd / 2 ); #pv WindowFromPhysicalPoint = ::GK.User32.GetEntry( "WindowFromPhysicalPoint" ); #pv hwnd = WindowFromPhysicalPoint( Xc, Yc ); if( hwnd == #NULL ) { .MessageBox( "対象のウィンドウがありません!", "画面キャプチャ", #MB_ICONEXCLAMATION | #MB_OK ); return; } //#pv GetParent = ::GK.User32.GetEntry( "GetParent" ); // while(( h = GetParent( hwnd )) != #NULL ) //#pv hwnd = h; class TmpWnd : ::GK.Window { .hWnd = hwnd; } Rt = ::GK.Rect(); if( full? ) // 移動先は、対象ウィンドウの全領域? { TmpWnd.GetWindowRect( Rt ); } else // 移動先は、対象ウィンドウ内のクライアント領域 { ( Rt.Xo, Rt.Yo ) = TmpWnd.ClientToScreen( 0, 0 ); ( Rt.Xd, Rt.Yd ) = TmpWnd.GetClientSize(); } #pc Rt.Xo, Rt.Yo, Rt.Xd, Rt.Yd; // 本ウィンドウのクライアント領域を、対象ウィンドウの領域へ移動 .GetWindowRect( Rw'cbox! ); ( Rc.Xo, Rc.Yo ) = .ClientToScreen( 0, 0 ); ( Rc.Xd, Rc.Yd ) = ( .gc.Xd, .gc.Yd ); // = .GetClientSize(); Rt.Shift( Rw.Xo - Rc.Xo, Rw.Yo - Rc.Yo, Rw.Xd - Rc.Xd, Rw.Yd - Rc.Yd ); #pc Rt.Xo, Rt.Yo, Rt.Xd, Rt.Yd; .Move( Rt ); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.ResizeClientArea( Xd, Yd ) { .GetWindowRect( R'cbox! ); R.Xd += Xd'cut( 120, ::GK.GetSystemMetrics( #SM_CXSCREEN )) - .gc.Xd; R.Yd += Yd'cut( 60, ::GK.GetSystemMetrics( #SM_CYSCREEN )) - .gc.Yd; .Move( R ); } //----------------------------------------------------------------------------- function ^DrawCursor( Xo, Yo ) // マウスカーソルを描画 { ^CursIconInfo.Seek(0); ^CursIconInfo.Write( ^CursInfoSize ); ^CursIconInfo.Seek(0); ^GetCursorInfo( ^CursIconInfo'addr ); ^CursIconInfo.Read( ^CURSORINFO ); //^CursIconInfo.Seek( ^CursInfoSize ); ^GetIconInfo( ^CURSORINFO.hCursor, ^CursIconInfo'addr ); ^CursIconInfo.Read( ^ICONINFO ); x = ^CURSORINFO.ptScreenPos_x - ^ICONINFO.xHotspot - Xo; y = ^CURSORINFO.ptScreenPos_y - ^ICONINFO.yHotspot - Yo; ^DrawIcon_( .hdc, x, y, ^CURSORINFO.hCursor ); } //----------------------------------------------------------------------------- function ^MainWindow.SaveCapToFile() { if( ! .CapFrame ) { ErrMsg = "画像が未だ取り込まれていません!"; goto ERR_MSG; } OFN.Title = "保存ファイル"; OFN.FileSpec'USHORT( #MAX_PATH + 4 ); OFN.FileSpec'puts( .CapFileName ); OFN.InitialDir = .CapFilePath; OFN.Filter'USHORT( 256 ); i = 0; i += OFN.Filter'puts( "BMPファイル(*.bmp)", i ) + 1; i += OFN.Filter'puts( "*.bmp", i ) + 1; i += OFN.Filter'puts( "全て(*.*)", i ) + 1; i += OFN.Filter'puts( "*.*", i ) + 1; OFN.Flags = #OFN_OVERWRITEPROMPT | #OFN_HIDEREADONLY | #OFN_NOCHANGEDIR | #OFN_ENABLESIZING | #OFN_PATHMUSTEXIST; if( ! .GetSaveFileName( OFN ) ) return; #pc "File Name = ": OFN.FileSpec'gets; file = ::File.Open( OFN.FileSpec'gets, "out" ); T := .CapFrame; if( .gc.Xd != T.Xd || .gc.Yd != T.Yd ) { frame = ::GK.VFrame( .gc.Xd, .gc.Yd, 24 ); frame.CopyImage( .gc, , , #SRCCOPY ); T := frame; } BITMAPFILEHEADER ::= { .Type 'C(2) = "BM"; .Size 'ULONG ; // 本BMPファイルのサイズ .Reserved1 'USHORT; .Reserved2 'USHORT; .OffBits 'ULONG ; // 画素データまでのオフセット } BITMAPINFOHEADER ::= { .Size 'ULONG = 40; .Width 'LONG = T.Xd; .Height 'LONG = T.Yd; .Planes 'USHORT = 1; .BitCount 'USHORT = 24; .Compression 'ULONG = #BI_RGB; .SizeImage 'ULONG = T.Bn; .XPelsPerMeter 'LONG ; .YPelsPerMeter 'LONG ; .ClrUsed 'ULONG; .ClrImportant 'ULONG; } BITMAPFILEHEADER.OffBits = BITMAPFILEHEADER'size + BITMAPINFOHEADER'size; BITMAPFILEHEADER.Size = BITMAPFILEHEADER.OffBits + T.Bn; file.Write( BITMAPFILEHEADER, BITMAPINFOHEADER ); file.Write( 'MEMBLK( T.Bp, T.Bn ) ); .CapFileName = file.name; .CapFilePath = file.path; file.Close(); return; OnFileError: ErrMsg = ::ErrorMessage( $err_code, $err_info ); ERR_MSG: .MessageBox( ErrMsg, "画像保存", #MB_ICONEXCLAMATION | #MB_OK ); return; } //----------------------------------------------------------------------------- function ^MainWindow.SaveCapToClip() { if( ! .CapFrame ) { .MessageBox( "画像が未だ取り込まれていません!", "画像保存", #MB_ICONEXCLAMATION | #MB_OK ); return; } T := .CapFrame; if( .gc.Xd != T.Xd || .gc.Yd != T.Yd ) { frame = ::GK.VFrame( .gc.Xd, .gc.Yd, 24 ); frame.CopyImage( .gc, , , #SRCCOPY ); T := frame; } sz = 40; // = sizeof( BITMAPINFO ) BITMAPINFO'ULONG( 10 ) = // BITMAPINFO ( = BITMAPINFOHEADER ) { sz, // biSize T.Xd, // biWidth T.Yd, // biHeight 1 + ( 24 << 16 ), // biPlanes, biBitCount #BI_RGB, // biCompression T.Bn, // biSizeImage 0, // biXPelsPerMeter 0, // biYPelsPerMeter 0, // biClrUsed 0, // biClrImportant }; #pv hMem = ::GK.GlobalAlloc( #GMEM_MOVEABLE | #GMEM_SHARE, sz + T.Bn ); if( hMem == NULL ) goto ERR_MSG; pMem = ::GK.GlobalLock( hMem ); if( pMem == NULL ) goto ERR_FIN; pMem'memcpy( BITMAPINFO'addr, sz ); ( pMem + sz )'memcpy( T.Bp, T.Bn ); ::GK.GlobalUnlock( hMem ); #pv OpenClipboard = ::GK.User32.GetEntry( "OpenClipboard" ); #pv EmptyClipboard = ::GK.User32.GetEntry( "EmptyClipboard" ); #pv SetClipboardData = ::GK.User32.GetEntry( "SetClipboardData" ); #pv CloseClipboard = ::GK.User32.GetEntry( "CloseClipboard" ); if( OpenClipboard( #NULL ) == #FALSE ) goto ERR_FIN; #pv EmptyClipboard(); #pv rv = SetClipboardData( #CF_DIB, hMem ); #pv CloseClipboard(); if( rv != #NULL ) { .MessageBox( "現表示画像をクリップボードに保存しました!", "画像保存", #MB_ICONINFORMATION | #MB_OK ); return; } ERR_FIN: ::GK.GlobalFree( hMem ); ERR_MSG: .MessageBox( ::GK.GetSysErrMsg(), "画像保存", #MB_ICONERROR | #MB_OK ); } //============================================================================= function ^MainWindow.ShowCameraView() { #pc "ShowCameraView: ": ::MK.GetCapDriverDesc(0); if( .CameraView ) // カメラ入力画像は既に表示済? goto FIN; if( ::MK.GetCapDriverDesc(0) == null ) { .MessageBox( "カメラ入力が装備されていません!", #MAIN_TITLE, #MB_ICONEXCLAMATION | #MB_OK ); return; } .SetWindowRgn( #NULL, #FALSE ); R = ::GK.Rect( 0, 0, .gc.Xd, .gc.Yd ); .gc.DrawEdge( R, 0, #BF_MIDDLE ); .gc.DrawText( R, "しばらくお待ちください" ); if( .CapFrame ) delete .CapFrame; #pc "CameraView=": .CameraView = .AddCaptureWindow( R ); if( .CameraView == null ) goto ERR_1; if( .CameraView.DriverConnect( 0 ) == #FALSE ) goto ERR_2; .CameraView.PreviewRate( 66 ); .CameraView.PreviewScale( #TRUE ); .CameraView.Preview( #TRUE ); .AdjWndSizeToCamera( #TRUE ); FIN: .ShowCapStatus( #ST_CAMERA_VIEW ); return; ERR_2: .CameraView.Destroy(); ERR_1: delete .CameraView; .SetWndRgnForCap(); .MessageBox( "カメラ入力ができません!", #MAIN_TITLE, #MB_ICONERROR | #MB_OK ); .ShowCapStatus(); } //----------------------------------------------------------------------------- function ^MainWindow.DlgCameraFormat() { if( ! .CameraView ) { .MessageBox( "この設定は、カメラ入力中にしかできません!", #MAIN_TITLE, #MB_ICONEXCLAMATION | #MB_OK ); return; } if( ! .CameraView.DlgVideoFormat() ) return; // 設定キャンセルの場合 .AdjWndSizeToCamera( #TRUE ); } //----------------------------------------------------------------------------- function ^MainWindow.AdjWndSizeToCamera( Ask? ) { cs = .CameraView.GetStatus(); if( cs == null ) return; if( cs.uiImageWidth == .gc.Xd && cs.uiImageHeight == .gc.Yd ) return; /*@@@* if( Ask? ) if( .MessageBox( "カメラの画像サイズにウィンドウのサイズを合わせますか?", #MAIN_TITLE, #MB_ICONQUESTION | #MB_YESNO ) != #IDYES ) return; } /*@@@*/ R = ::GK.Rect( 0, 0, cs.uiImageWidth, cs.uiImageHeight ); .CameraView.Move( R ); .GetWindowRect( R ); R.Xd += cs.uiImageWidth - .gc.Xd; R.Yd += cs.uiImageHeight - .gc.Yd; .Move( R ); } //============================================================================= #set VideoTitle "VideoCapt.mc" #set CHARx4(c1,c2,c3,c4) (#c1+(#c2<<8)+(#c3<<16)+(#c4<<24)) #set streamtypeVIDEO #CHARx4( `v`,`i`,`d`,`s` ) #set comptypeDIB #CHARx4( `D`,`I`,`B`,` ` ) #set AVIERR_OK 0 #set OF_WRITE 0x00000001 #set OF_CREATE 0x00001000 #set OF_SHARE_DENY_NONE 0x00000040 #set AVIIF_KEYFRAME 0x00000010 #set AVICOMPRESSF_INTERLEAVE 0x00000001 #set AVICOMPRESSF_DATARATE 0x00000002 #set AVICOMPRESSF_KEYFRAMES 0x00000004 #set ICMODE_COMPRESS 1 #set ICMF_COMPVARS_VALID 0x00000001 // COMPVARS contains valid data // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.OpenAviFileStream() { Xd = .gc.Xd; Yd = .gc.Yd; Zd = 24; Bn = ((( Xd * Zd + 7 ) / 8 + 3 ) & ~3 ) * Yd; Rate = .SampleRate; comp_vars := .GetCurCompressor(); // 現在選定されている画像圧縮処理系 if( ! comp_vars ) goto ERR_FIN; .AviFile'ULONG; .AviStrm'ULONG; ::MK.AVIFileInit(); AviFullName = .AviFilePath + .AviFileName; #pv ::File.Delete( AviFullName ); rv = ::MK.AVIFileOpen( .AviFile'addr, AviFullName, #OF_CREATE | #OF_WRITE | #OF_SHARE_DENY_NONE, #NULL ); if( rv != #AVIERR_OK ) goto ERR_FIN; AVISTREAMINFO ::= { .fccType 'ULONG = #streamtypeVIDEO; .fccHandler 'ULONG = comp_vars.fccHandler; .dwFlags 'ULONG = 0; .dwCaps 'ULONG = 0; .wPriority 'SHORT = 0; .wLanguage 'SHORT = 0; .dwScale 'ULONG = 1; .dwRate 'ULONG = Rate; .dwStart 'ULONG = 0; .dwLength 'ULONG = 0; .dwInitialFrames 'ULONG = 0; .dwSuggestedBufferSize 'ULONG = 0; .dwQuality 'ULONG = -1; .dwSampleSize 'ULONG = 0; .rcFrame 'ULONG(4) = { 0, 0, Xd, Yd }; // RECT .dwEditCount 'ULONG = 0; .dwFormatChangeCount 'ULONG = 0; .szName 'T(64) = #VideoTitle; } avis_info = ::Buffer(); avis_info.Write( AVISTREAMINFO ); avis_info.Seek( 0 ); strm'ULONG; rv = ::MK.AVIFileCreateStream( .AviFile, strm'addr, avis_info'addr ); if( rv != #AVIERR_OK ) goto ERR_FIN; if( comp_vars.hic ) // 画像圧縮あり? { AVICOMPRESSOPTIONS ::= { .fccType 'ULONG = #streamtypeVIDEO; .fccHandler 'ULONG = comp_vars.fccHandler; .dwKeyFrameEvery 'ULONG = comp_vars.lKey; .dwQuality 'ULONG = comp_vars.lQ; .dwBytesPerSecond 'ULONG = comp_vars.lDataRate; .dwFlags 'ULONG = #AVICOMPRESSF_DATARATE | #AVICOMPRESSF_KEYFRAMES; .lpFormat 'ULONG = #NULL; .cbFormat 'ULONG = 0; .lpParms 'ULONG = #NULL; .cbParms 'ULONG = 0; .dwInterleaveEvery 'ULONG = 0; } avic_opts = ::Buffer(); avic_opts.Write( AVICOMPRESSOPTIONS ); avic_opts.Seek( 0 ); rv = ::MK.AVIMakeCompressedStream( .AviStrm'addr, strm, avic_opts'addr, #NULL ); ::MK.AVIStreamRelease( strm ); if( rv != #AVIERR_OK ) goto ERR_FIN; } else // 無圧縮の場合 .AviStrm = strm; bmih 'ULONG(10) = // BITMAPINFOHEADER { 40, // DWORD biSize; = sizeof(BITMAPINFOHEADER) Xd, // LONG biWidth; Yd, // LONG biHeight; 1 + // WORD biPlanes; ( Zd << 16 ), // WORD biBitCount; 0, // DWORD biCompression; Bn, // DWORD biSizeImage; 0, // LONG biXPelsPerMeter; 0, // LONG biYPelsPerMeter; 0, // DWORD biClrUsed; 0 // DWORD biClrImportant; }; rv = ::MK.AVIStreamSetFormat( .AviStrm, 0, bmih'addr, bmih'size ); if( rv != #AVIERR_OK ) goto ERR_FIN; return #TRUE; ERR_FIN: .MessageBox( "AVIファイルが開けません!\n" + AviFullName + "\n" "0x" + rv'x(8), #MAIN_TITLE, #MB_ICONERROR | #MB_OK ); .CloseAviFileStream(); return #FALSE; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.CloseAviFileStream() { if( .AviStrm ) ::MK.AVIStreamRelease( .AviStrm ); if( .AviFile ) { ::MK.AVIFileRelease( .AviFile ); ::MK.AVIFileExit(); } delete .AviStrm, .AviFile; } //----------------------------------------------------------------------------- function ^MainWindow.GetCurCompressor() // 現在の画像圧縮系を返す { if( ! .COMPVARS ) { .COMPVARS ::= { .cbSize 'LONG ; // LONG sizeof(COMPVARS) .dwFlags 'ULONG; // DWORD ICMF_CHOOSE_... .hic 'ULONG; // HIC HIC of chosen compressor .fccType 'ULONG; // DWORD basically ICTYPE_VIDEO .fccHandler 'ULONG; // DWORD handler of chosen compressor or "" or "DIB " .lpbiIn 'ULONG; // LPBITMAPINFO input format .lpbiOut 'ULONG; // LPBITMAPINFO output format - will compress to this .lpBitsOut 'ULONG; // LPVOID .lpBitsPrev 'ULONG; // LPVOID .lFrame 'LONG ; // LONG .lKey 'LONG ; // LONG key frames how often? .lDataRate 'LONG ; // LONG desired data rate KB/Sec .lQ 'LONG ; // LONG desired quality .lKeyCount 'LONG ; // LONG .lpState 'ULONG; // LPVOID state of compressor .cbState 'LONG ; // LONG size of the state } .COMPVARS.cbSize = .COMPVARS'size; .COMPVARS.fccHandler = #comptypeDIB; } return .COMPVARS'ref; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.DlgChooseCompressor() // 画像圧縮方式を選ぶ { compvars = ::Buffer(); compvars.Write( .GetCurCompressor()'val ); compvars.Seek( 0 ); rv = ::MK.ICCompressorChoose( #NULL, 0, #NULL, #NULL, compvars'addr, #NULL ); if( ! rv ) return #FALSE; compvars.Read( .COMPVARS ); /*@@@* .COMPVARS ::= { #p " cbSize ": .cbSize :"\n": " dwFlags ": .dwFlags'x :"\n": " hic ": .hic'x :"\n": " fccType ": .fccType'cbo'c :"\n": " fccHandler ": .fccHandler'cbo'c :"\n": " lpbiIn ": .lpbiIn :"\n": " lpbiOut ": .lpbiOut :"\n": " lpBitsOut ": .lpBitsOut :"\n": " lpBitsPrev ": .lpBitsPrev :"\n": " lFrame ": .lFrame :"\n": " lKey ": .lKey :"\n": " lDataRate ": .lDataRate :"\n": " lQ ": .lQ :"\n": " lKeyCount ": .lKeyCount :"\n": " lpState ": .lpState :"\n": " cbState ": .cbState ; } /*@@@*/ .COMPVARS.cbSize = .COMPVARS'size; //(念のため) return #TRUE; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.FreeCurCompressor() // 現在の画像圧縮処理系を解除 { if( .COMPVARS.hic ) { compvars = ::Buffer(); compvars.Write( .COMPVARS ); compvars.Seek( 0 ); ::MK.ICCompressorFree( compvars'addr ); .COMPVARS.hic = #NULL; } } //----------------------------------------------------------------------------- function ^MainWindow.CanRecStart?() { if( .AviFile ) // 録画中? return #FALSE; if( .CapFrame ) { .MessageBox( "録画は、透明窓かカメラ入力画面の時に実行できます。", #MAIN_TITLE, #MB_ICONEXCLAMATION | #MB_OK ); return #FALSE; } return #TRUE; } //----------------------------------------------------------------------------- function ^MainWindow.StartRecordMovie() { #pc "StartRecordMovie"; if( ! .CanCapStart?() ) return; .StopTimer( #TM_RECORD ); .FrameCount = 0; .RecSec = 0; if( ! .OpenAviFileStream() ) { .ShowCapStatus( #ST_REC_MOVIE_ERROR ); return; } .RecView = ::GK.WndView(); // デスクトップの画面 .RecRect = ::GK.Rect( 0, 0, .gc.Xd, .gc.Yd ); ( .RecRect.Xo, .RecRect.Yo ) = .ClientToScreen( 0, 0 ); .RecFrame = ::GK.VFrame( .gc.Xd, .gc.Yd, 24 ); if( ! .RecordMovieFrame() ) return; .ShowCapStatus( #ST_REC_MOVIE_ON ); .StartTimer( #TM_REC_FRAME, 1000 / .SampleRate ); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.RecordMovieFrame() { #pc "RecordMovieFrame:", .FrameCount; .RecFrame.CopyImage( .RecView, , .RecRect, #SRCCOPY ); if( ! .CameraView ) .RecFrame.[ ^DrawCursor ]( .RecRect.Xo, .RecRect.Yo ); rv = ::MK.AVIStreamWrite( .AviStrm, .FrameCount++, 1, .RecFrame.Bp, .RecFrame.Bn, #AVIIF_KEYFRAME, #NULL, #NULL ); if( rv != #AVIERR_OK ) { .StopRecordMovie( #TRUE ); return null; } if( .FrameCount * 1000 / .SampleRate >= 1000 * ( .RecSec + 1 )) { if( ++.RecSec >= #MAX_RECORD_TIME ) { .StopRecordMovie( #FALSE ); return #FALSE; } .ShowCapStatus( #ST_REC_MOVIE_ON ); } return #TRUE; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.StopRecordMovie( error? ) { if( ! .AviFile ) // 録画中でない? return; .StopTimer( #TM_REC_FRAME ); .CloseAviFileStream(); delete .RecFrame, .RecView, .RecRect; if( error? ) { .ShowCapStatus( #ST_REC_MOVIE_ERROR ); .MessageBox( "AVIファイル書き込みエラー発生!", #MAIN_TITLE, #MB_ICONERROR | #MB_OK ); } else { .RecSec = ( .FrameCount + .SampleRate / 2 ) / .SampleRate; .ShowCapStatus( #ST_REC_MOVIE_DONE ); } } //----------------------------------------------------------------------------- function ^MainWindow.DlgChooseFileToSave() { OFN.Title = "動画の保存ファイル"; OFN.FileSpec'USHORT( #MAX_PATH + 4 ); OFN.FileSpec'puts( .AviFileName ); OFN.InitialDir = .AviFilePath; OFN.Filter'USHORT( 256 ); i = 0; i += OFN.Filter'puts( "AVIファイル(*.avi)", i ) + 1; i += OFN.Filter'puts( "*.avi", i ) + 1; i += OFN.Filter'puts( "全て(*.*)", i ) + 1; i += OFN.Filter'puts( "*.*", i ) + 1; OFN.Flags = #OFN_OVERWRITEPROMPT | #OFN_HIDEREADONLY | #OFN_NOCHANGEDIR | #OFN_ENABLESIZING | #OFN_PATHMUSTEXIST; if( ! .GetSaveFileName( OFN ) ) return; #pc "File Name = ": OFN.FileSpec'gets; ::File.GetNamePart( OFN.FileSpec'gets, .AviFilePath, .AviFileName, XN'new! ); #pv .AviFileName += XN; #pc .AviFilePath; } //============================================================================= /// ウィンドウのクライアントサイズ選択ダイアログウィンドウ /// function ^DlgWnd_ClientSize.OnDisplay() { #pc "DlgWnd_ClientSize.OnDisplay"; .DefWidgetFont := ^DlgFont; #set DY 40 ( .CX, .CY ) = .Owner.GetClientSize(); R1 = ::GK.Rect( 20, 28, 30, 20 ); R2 = ::GK.Rect( 50, 24, 60, 100 ); .AddLabel( "横:", R1 ); .CB_Xd = .AddComboBox( #CBS_DROPDOWN, R2 ); .CB_Xd.Add( "160" ); .CB_Xd.Add( "320" ); .CB_Xd.Add( "640" ); .CB_Xd.SetText( .CX'd ); R1.Yo += #DY; R2.Yo += #DY; .AddLabel( "縦:", R1 ); .CB_Yd = .AddComboBox( #CBS_DROPDOWN, R2 ); .CB_Yd.Add( "120" ); .CB_Yd.Add( "240" ); .CB_Yd.Add( "480" ); .CB_Yd.SetText( .CY'd ); R3 = ::GK.Rect( R2.Xo + R2.Xd + 24, 22, 90, 26 ); .B_160x120 = .AddButton( #BS_PUSHBUTTON, "160 x 120", R3 ); .B_160x120.OnCommand = function() { .owner.CB_Xd.SetText("160"); .owner.CB_Yd.SetText("120"); }; R3.Yo += #DY; .B_320x240 = .AddButton( #BS_PUSHBUTTON, "320 x 240", R3 ); .B_320x240.OnCommand = function() { .owner.CB_Xd.SetText("320"); .owner.CB_Yd.SetText("240"); }; R3.Yo += #DY; .B_640x480 = .AddButton( #BS_PUSHBUTTON, "640 x 480", R3 ); .B_640x480.OnCommand = function() { .owner.CB_Xd.SetText("640"); .owner.CB_Yd.SetText("480"); }; R3.Yo += #DY; .B_CurXdYd = .AddButton( #BS_PUSHBUTTON, "現サイズ", R3 ); .B_CurXdYd.OnCommand = function() { .owner.CB_Xd.SetText( .owner.CX'd ); .owner.CB_Yd.SetText( .owner.CY'd ); }; R = ::GK.Rect( R2.Xo - 20, R2.Yo + #DY, 80, 26 ); .B_OK = .AddButton( #BS_PUSHBUTTON, "OK", R ); .B_OK.OnCommand = function() { .owner.Result = #TRUE; .owner.CX = .owner.CB_Xd.GetText()'int; .owner.CY = .owner.CB_Yd.GetText()'int; .owner.Close(); }; R.Yo += #DY; .B_Cancel = .AddButton( #BS_PUSHBUTTON, "キャンセル", R ); .B_Cancel.OnCommand = function() { .owner.Close(); }; } function ^DlgWnd_ClientSize.OnEnd() { #pc "DlgWnd_ClientSize.OnEnd: Result=": .Result, "CX=": .CX, "CY=": .CY; if( .Result ) .Owner.ResizeClientArea( .CX, .CY ); } //----------------------------------------------------------------------------- /// サンプリング周期選択ダイアログウィンドウ /// function ^DlgWnd_SampleRate.OnDisplay() { #pc "DlgWnd_SampleRate.OnDisplay"; .DefWidgetFont := ^DlgFont; R1 = ::GK.Rect( 28, 32, 70, 100 ); .CB = .AddComboBox( #CBS_DROPDOWNLIST, R1 ); for( i = #MIN_SAMPLE_RATE ; i <= #MAX_SAMPLE_RATE ; i++ ) .CB.Add( i'd's(4) ); .CB.Select( .Owner.SampleRate - 1 ); R2 = ::GK.Rect( R1.Xo + R1.Xd + 10, R1.Yo + 6, 60, 20 ); .AddLabel( "回/秒", R2 ); R = ::GK.Rect( R2.Xo + R2.Xd + 8, R1.Yo - 18, 80, 26 ); .B_OK = .AddButton( #BS_PUSHBUTTON, "OK", R ); .B_OK.OnCommand = function() { .owner.Result = #TRUE; .owner.SampleRate = .owner.CB.GetText()'int; .owner.Close(); }; R.Yo += 34; .B_Cancel = .AddButton( #BS_PUSHBUTTON, "キャンセル", R ); .B_Cancel.OnCommand = function() { .owner.Close(); }; } function ^DlgWnd_SampleRate.OnEnd() { #pc "DlgWnd_SampleRate.OnEnd: Result=": .Result, "Rate=": .SampleRate; } //-----------------------------------------------------------------------------