/* =============================================================================== CalcB60.mc : 60進数電卓 =============================================================================== Copyright (C) 2010-2013 Masahiko Watanabe Edition History: 2010.10.28 初版( GK Library: Rev.2009.02.03 使用 ) 2013.02.15 MikoScript3(ユニコード版)用に変更 -----------------------------------------------------------------------------*/ /*****/ // GK Library のモジュールをロードする場合 #include #include #include if( ::Module.Include( "GkLibrary" ) > 0 ) Main( argc, argv ); return; /*****/ /***** // GK Library のソースをインクルードする場合 #include Main( argc, argv ); return; /*****/ //----------------------------------------------------------------------------- // デバッグプリント無効化(リリース時) #set pc #comment #set pv // デバッグプリント有効化(デバッグ時) //#set pc print //#set pv print #set p print //----------------------------------------------------------------------------- #set WndXd 240 // ウィンドウ幅 #set WndYd 250 // ウィンドウ高 #set DspXd 200 // 入力数値/計算結果の表示枠の幅 #set DspYd 30 // 同表示枠の高さ #set BtnXd 32 // ボタン幅 #set BtnYd 32 // ボタン高 #set BtnXs 8 // ボタンの左右の隙間 #set BtnYs 8 // ボタンの上下の隙間 #set MaxIn 12 // 最大入力字数(先頭マイナス符号は除く) #set MaxFn 10 // 小数点以下の最大有効桁数 // 指令コード #set CMD_END 0x100 #set CMD_SIGN 0x101 #set CMD_CLEAR 0x102 #set CMD_ALLCLR 0x103 #set CMD_BACK 0x104 //----------------------------------------------------------------------------- function ^Main( argc, argv ) { #pc "開始"; ^DefValue = null; ^NumFont = ::GK.Font( "Courier New", 20, #ANSI_CHARSET, #FIXED_PITCH, ::GK.Font.BOLD ); // "MS 明朝", 20, #SHIFTJIS_CHARSET, #FIXED_PITCH ); ^BtnFont = ::GK.Font( "MS ゴシック", 15, #SHIFTJIS_CHARSET, #FIXED_PITCH, ::GK.Font.BOLD ); // "MS 明朝", 14, #SHIFTJIS_CHARSET, #FIXED_PITCH ); class ^CalcWindow : ::GK.Window {} #pv ^MainWnd = ^CalcWindow(); ^MainWnd.Open(); ::GK.WindowMsgLoop(); FIN: delete ^MainWnd; delete ^NumFont, ^BtnFont; #pc "終了"; return; OnError: print ::ErrorMessage( $err_code, $err_info ); goto FIN; } //----------------------------------------------------------------------------- function ^CalcWindow.Construct() { .[ ::GK.Window.Construct ]( "60進数電卓", // 画面中央に表示する場合は、以下のコメントのように変更 70, //( ::GK.GetSystemMetrics( #SM_CXSCREEN ) - #WndXd ) / 2, 20, //( ::GK.GetSystemMetrics( #SM_CYSCREEN ) - #WndYd ) / 2, #WndXd, #WndYd, #WS_POPUP | #WS_VISIBLE | #WS_CAPTION | #WS_DLGFRAME | #WS_SYSMENU ); } function ^CalcWindow.Destruct() { .[ ::GK.Window.Destruct ](); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^CalcWindow.OnCreate() { #pc "OnCreate"; #pv ( .Xd, .Yd ) = .GetClientSize(); .DspRect = ::GK.Rect( ( .Xd - #DspXd ) / 2, 12, #DspXd, #DspYd ); .NumRect = .DspRect; .NumRect.Shift( +8, +5, -20, -8 ); .gc.SetFont( ^NumFont ); .gc.SetBackMode( #TRANSPARENT ); .DefWidgetFont := ^BtnFont; Name = { { "7", "8", "9", "+", "±" }, { "4", "5", "6", "−", "C" }, { "1", "2", "3", "×", "AC" }, { "0", ":", "・", "÷", "=" }, }; Cmd = { { `7`, `8`, `9`, `+`, #CMD_SIGN }, { `4`, `5`, `6`, `-`, #CMD_CLEAR }, { `1`, `2`, `3`, `*`, #CMD_ALLCLR }, { `0`, `:`, `.`, `/`, `=` }, }; Color = { { #RGB(224,224,255), #RGB(224,224,255), #RGB(224,224,255), #RGB(224,255,128), #RGB(255,224, 96) }, { #RGB(224,224,255), #RGB(224,224,255), #RGB(224,224,255), #RGB(224,255,128), #RGB(255,208, 96) }, { #RGB(224,224,255), #RGB(224,224,255), #RGB(224,224,255), #RGB(224,255,128), #RGB(255,208, 96) }, { #RGB(224,224,255), #RGB(224,208,224), #RGB(208,208,208), #RGB(224,255,128), #RGB(255,224,112) }, }; BtnXo = ( .Xd - ( #BtnXd * 5 + #BtnXs * 4 )) / 2; R = ::GK.Rect( BtnXo, 10 + #DspYd + 14, #BtnXd, #BtnYd ); for( v = 0 ; v < 4 ; v++ ) { for( h = 0 ; h < 5 ; h++ ) { name = Name[v][h]; #pv .Btn[ name ] = .AddButton( "draw", name, R ); .Btn[ name ].OnCommand = function() { .owner.SetFocus(); .owner.ExecCmd( .Cmd ); }; .Btn[ name ].Cmd = Cmd[v][h]; .Btn[ name ].OnDraw = function( s ) { .owner.OnDraw( this, s ); }; .Btn[ name ].Rect = R; .Btn[ name ].Color = Color[v][h]; R.Xo += #BtnXd + #BtnXs; } R.Yo += #BtnYd + #BtnYs; R.Xo = BtnXo; } .state = 0; // =0: READY, =1: 第1項入力中, =2: 第2項入力中 .Cs'USHORT( #MaxIn + 4 ); // 入力文字列バッファ .ResetInput(); .A = 0; // 計算結果の値 .Input? = #FALSE; // =TRUE: 入力文字列表示中, =FALSE: 計算結果表示中 .Decimal? = #FALSE; // =TRUE: 10進数表示, =FALSE: 60進数表示 .MaxVal = ::Math.pow( 10, #MaxIn ) - 1; // 扱う数値の最大値 .MinVal = ::Math.pow( 10, -#MaxFn ) / 2; // 扱う正値の最小値 } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^CalcWindow.OnClose() { #pc "OnClose"; return 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^CalcWindow.OnMinMaxInfo( pMinMax ) { #pc "OnMinMaxInfo"; //MINMAXINFO ::= //{ // .Reserved = POINT; // .MaxSize = POINT; // .MaxPosition = POINT; // .MinTrackSize = POINT; // .MaxTrackSize = POINT; //} // ウィンドウサイズが変更できないように // .MinTrackSize と .MaxTrackSize を設定して返す P'LONG(4) = { #WndXd, #WndYd, #WndXd, #WndYd }; ( pMinMax + 3*4*2 )'memcpy( P'addr, 4*4 ); return #TRUE; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^CalcWindow.OnPaint() { #pc "OnPaint"; .GetHotRect( R'STRUCT ); .gc.DrawEdge( R, 0, #BF_MIDDLE ); if( .Input? ) // 入力文字列表示中? .ShowInput(); else // 計算結果表示中 .ShowResult(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^CalcWindow.OnDraw( btn, status ) { R := btn.Rect; #pc "OnDraw: " : btn'name, status, R; .gc.ClearRect( R, btn.Color ); .gc.SetFont( ^BtnFont ); switch( status ) { default: case 0: .gc.DrawEdge( R, #EDGE_RAISED, #BF_RECT ); // 通常 .gc.DrawText( R, btn'name ); break; case 1: .gc.DrawEdge( R, #EDGE_SUNKEN, #BF_RECT ); // 押下 Rz = R; Rz.Xo += 1; Rz.Yo += 1; .gc.DrawText( Rz, btn'name ); break; //case 2: // 禁止 } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^CalcWindow.OnChar( c ) { #pc "OnChar", c'c; .ExecCmd( c ); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^CalcWindow.OnKeyDown( key ) { #pc "OnKeyDown", key'x(4); switch( key & ~#KCF_REPEAT ) { case #VK_BACK: .ExecCmd( #CMD_BACK ); break; case #VK_DELETE: .ExecCmd( #CMD_CLEAR ); break; case #VK_ESCAPE: .ExecCmd( #CMD_END ); break; case #VK_RETURN: .ExecCmd( `=` ); break; } } //----------------------------------------------------------------------------- function ^CalcWindow.ExecCmd( c ) { switch( c ) { case `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`: if( .state == 0 ) .state = 1; if( .In == 1 && .Cs(0) == `0` ) // 0 の1字だけ入力? .In = 0; INPUT_CHAR: if( .In >= #MaxIn ) goto FIN; if( .Fn >= 0 && c != `.` ) // 小数点以下の数字入力? { if( .Fn >= #MaxFn ) goto FIN; .Fn++; } .Cs( .In++ ) = c; SHOW_INPUT: .ShowInput(); goto FIN; case `:`: if( .state == 0 ) goto FIN; if( .Fn >= 0 ) goto FIN; // 小数点以下入力中 if( .In == 0 ) .Cs( .In++ ) = `0`; goto INPUT_CHAR; case `.`: if( .state == 0 ) .state = 1; else if( .Fn >= 0 ) // 小数点以下入力中? goto FIN; .Fn = 0; // = 小数点以下の入力桁数 if( .In == 0 ) .Cs( .In++ ) = `0`; goto INPUT_CHAR; case #CMD_SIGN: if( .Input? ) { .S = ! .S; goto SHOW_INPUT; } .A = -.A; .ShowResult(); goto FIN; case #CMD_BACK: if( .state == 0 ) goto ALL_CLEAR; if( .In > 0 ) .In--; if( .Fn >= 0 ) .Fn--; goto SHOW_INPUT; case #CMD_CLEAR: if( .state == 0 ) goto ALL_CLEAR; .ResetInput(); goto SHOW_INPUT; ALL_CLEAR: case #CMD_ALLCLR: .state = 0; .ResetInput(); .A = 0; .ShowResult(); goto FIN; case `+`, `-`, `*`, `/`: switch( .state ) { case 0: if( .Input? ) goto SET_ACC; if( .Decimal? ) .ShowResult(); break; SET_ACC: case 1: .A = .GetInputValue(); .ShowResult(); break; case 2: if( .In <= 0 ) goto SET_OP; call Calculate; } .state = 2; .ResetInput(); SET_OP: .Op = c; goto FIN; case `=`: switch( .state ) { case 0: .ShowResult( #TRUE ); // 60進⇔10進 を交互に切換 break; case 1: .A = .GetInputValue(); .ShowResult(); break; case 2: call Calculate; } .state = 0; .ResetInput(); goto FIN; Calculate: v = ( .Input? ? .GetInputValue() : .A ); DefOpValue = null; switch( .Op ) { case `+`: .A += v; break; case `-`: .A -= v; break; case `*`: .A *= v; break; case `/`: .A = .A / v'float; break; } .ShowResult(); back; case #CMD_END: .Close(); goto FIN; } FIN:; #pc ##ExecCmd: ${c'x(3)}, state=${.state}, Input?=${.Input?}, .A=${.A}##; } //----------------------------------------------------------------------------- function ^CalcWindow.ResetInput() { .In = 0; // 現在の入力文字数 or 入力インデックス(マイナス符号は除く) .Fn = -1; // 現在の小数点以下の入力桁数( =0: 小数点のみ, <0: 整数部のみ ) .S = #FALSE; // マイナス符号の有無 } function ^CalcWindow.GetInputValue() { .Cs( .In ) = 0; n = 0.0; k = 0.0; for( i = 0 ;; ) { switch( c = .Cs( i++ ) ) { case `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`: k = k * 10 + ( c - `0` ); continue; case `:`: n = ( n + k ) * 60; k = 0.0; continue; case `.`: quit; case 0: n += k; goto FIN; } } n += k; f = 0.0; d = 0; for( ;; ) { switch( c = .Cs( i++ ) ) { case `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`: f = f * 10 + ( c - `0` ); d++; continue; case 0: quit; } } n += f / ::Math.pow( 10, d ); FIN: #pc "GetInputValue: " : n, "%-12g"'fmt( n'float ), .S; return .S ? -n : n ; } function ^CalcWindow.ShowInput() { .Cs( .In ) = 0; T = ( .In > 0 ) ? .Cs'gets : "0" ; if( .S ) T = "-" + T; .gc.ClearRect( .DspRect ); .gc.SetFont( ^NumFont ); .gc.DrawText( .NumRect, T, #DT_RIGHT | #DT_VCENTER | #DT_SINGLELINE ); .gc.DrawEdge( .DspRect, #EDGE_SUNKEN, #BF_RECT ); .Input? = #TRUE; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^CalcWindow.ShowResult( CanDecimal? ) { if( CanDecimal? ) .Decimal? = ! .Decimal?; else .Decimal? = #FALSE; #pc "ShowResult: " : .A, "%-12g"'fmt( .A'float ), .Decimal? ; if( .A == null ) { ERROR: T = "- Error -"; .A = null; goto DISPLAY; } v = (( s = ( .A < 0 )) ? -.A : .A )'float; n = ::Math.floor( v ); // 整数部 f = v - n; // 小数部 if( n > .MaxVal ) goto ERROR; if( v < .MinVal ) { T = "0"; .A = 0; goto DISPLAY; } // 整数部の表示字数を求める i = 0; if( .Decimal? ) // 10進数表示? { i = ::Math.log10( .A )'int; if( i <= 0 ) i = 1; } else // 60進数表示 { for( k = n ; k >= 60 ; k = ::Math.floor( k / 60 ) ) { i += 3; } i += ( k < 10 ) ? 1 : 2; } if( i > #MaxIn ) goto ERROR; // 小数点以下の表示桁を求める d = #MaxIn - 1 - i; if( d > #MaxFn ) d = #MaxFn; e = ::Math.pow( 10, d ); m = ::Math.round( f * e ); // 小数点以下の有効表示桁の整数化値 if( m >= e ) // 四捨五入で最上位の桁上がりがあった? { if( ++n > .MaxVal ) goto ERROR; m = 0; d = -1; } // 整数部の表示桁を設定 if( .Decimal? ) // 10進数表示? { T = n'f(0); } else // 60進数表示 { r = ( n % 60 )'int; T = r'd; while(( n = ::Math.floor( n / 60 )) > 0 ) { k = r; r = ( n % 60 )'int; T = r'd + (( k < 10 ) ? ":0" : ":" ) + T; } } // 小数点以下の表示桁を設定 if( m > 0 ) { T += ".%0*.0f"'fmt( d, m )'trim( +1, "0." ); } if( s ) T = "-" + T; DISPLAY: .gc.ClearRect( .DspRect ); .gc.SetFont( ^NumFont ); .gc.DrawText( .NumRect, T, #DT_RIGHT | #DT_VCENTER | #DT_SINGLELINE ); .gc.DrawEdge( .DspRect, #EDGE_SUNKEN, #BF_RECT ); .Input? = #FALSE; } //-----------------------------------------------------------------------------