簡易関数電卓JsCalcWを改良してみた。(2)

 簡易関数電卓JsCalcWをさらにもうちょっと改良してみました。(^_^;
 何かちょっと足らないと思っていたら、[Ans]アンサーキーを付けるのを忘れていました。(^_^;
 ついでに、[M]キーも電卓っぽい感じが出るように、[MC],[MR],[MS],[M+],[M-]と増やしてみました。(^_^;
 おまけに、関数キーもいくつか([Rad],[Deg],[Rnd],[x!],[int])追加しておきました。(^_^;
P.S.
 メモリーをただの変数 M から内部メモリーに変更しました。仕様はソースを参照して下さい。(^_^;

● JsCalcW5.java

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

import java.util.*;
import javax.script.*;

public class JsCalcW5 {                         // JsCalcW.java [java]
    public static void main(final String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
    
    private static void createAndShowGUI() {
        // フレームの作成
        JFrame frame = new JFrame("JsCalcW");   // JFrameオブジェクトを生成
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                                                // ウィンドウを閉じるとプログラムが終了
        MyPanel h = new MyPanel();
        frame.add(h, BorderLayout.CENTER);      // フレームにオブジェクトを置く
        frame.pack();                           // フレームを必要最小の大きさにする
        frame.setMinimumSize(new Dimension(frame.getSize().width, frame.getSize().height));
                                                // 最小サイズを指定 1.6以上で有効
        frame.setAlwaysOnTop(true);             // 最前面ウィンドウへ設定します
        frame.setVisible(true);                 // フレームを表示する
    }
}

class MyPanel extends JPanel implements ActionListener { // JPanelを継承
    JButton    btn1, btn2;
    JTextField txt1, txt2;
    JLabel lbl1 = new JLabel();
    ArrayList<String> sHis = new ArrayList<String>();
    int iCnt = 0;
    JsCalc jsc = new JsCalc();
    String Ans = "", Mem = "0.0";
    
    // 追加ボタンの設定
    String [] sBtD = {                          // 表示用
        "arc","hyp","sin","cos","tan",
        "Rad","Deg","Rnd","x!","int",
        "x^y","10^x","exp","ln","log",
        "Ans","1/x","3√","π","e",
        "MC","MR","MS","M+","M−",
        "7","8","9","÷","CE",
        "4","5","6","×","√",
        "1","2","3","−","%",
        "0",".","(","+",")"
    };
    String [] sBtO = {                          // 出力用
        "a","h()","sin()","cos()","tan()",
        "Rad()","Deg()","Rnd()","fact()","floor()",
        "pow(,)","ten()","exp()","ln()","log10()",
        "","rcp()","cbrt()","PI","E",
        "","","","","",
        "7","8","9","/","",
        "4","5","6","*","sqrt()",
        "1","2","3","-","%",
        "0",".","(","+",")"
    };

    int nBtn = sBtD.length;                     // 追加するボタンの数
    JButton[] aBtn = new JButton[nBtn];         // 追加ボタン
    
    public MyPanel() {
        setBackground(SystemColor.control);     // 背景を灰色にする

        txt1 = new JTextField(14);              // txtの設定
        txt1.addActionListener(this);
        txt1.addKeyListener(new MyKeyListener());   // リスナーオブジェクトを指定
        txt1.setFont(new Font("SansSerif",Font.PLAIN,16));

        txt2 = new JTextField(14);              // txtの設定
        txt2.setFont(new Font("SansSerif",Font.ITALIC,16));
        txt2.setHorizontalAlignment(JTextField.RIGHT);
        
        btn1 = new JButton("=");
        btn1.addActionListener(this);
        btn1.addMouseListener(new MyMouseAdapter());    // ボタン上での右クリック

        btn2 = new JButton("C");
        btn2.addActionListener(this);
        btn2.addMouseListener(new MyMouseAdapter());    // ボタン上での右クリック

        for(int i = 0; i< nBtn; i++)
            aBtn[i] = new JButton(sBtD[i]);
        for(JButton b : aBtn)
            b.addActionListener(this);

        JPanel pa = new JPanel();
        pa.setLayout(new BorderLayout(2,2));
        pa.add(txt1, BorderLayout.NORTH);
        pa.add(txt2, BorderLayout.CENTER);
        pa.add(lbl1, BorderLayout.WEST);
        
        JPanel pb = new JPanel();
        pb.setLayout(new GridLayout(2,1,2,2));
        pb.add(btn1);
        pb.add(btn2);
        
        JPanel pc = new JPanel();
        pc.setLayout(new GridLayout(nBtn/5,5,2,2));
        for(JButton b : aBtn)
            pc.add(b);
        
        setLayout(new BorderLayout(2,2));
        add(pa, BorderLayout.CENTER);
        add(pb, BorderLayout.EAST);
        add(pc, BorderLayout.SOUTH);
        
        sHis.add(""); iCnt = sHis.size()-1;
    }

    public void actionPerformed(ActionEvent e) {    // ボタンクリック
        if(e.getSource()==btn1||e.getSource()==txt1) {
            String s = txt1.getText(); sHis.add(s); iCnt = sHis.size()-1;
            Ans = jsc.sEval(s); txt2.setText(Ans);
            txt1.select(0,s.length());
        } else if(e.getSource()==btn2) {
            txt1.setText(""); txt2.setText("");
        } else if(e.getSource()==aBtn[Arrays.asList(sBtD).indexOf("CE")]) {
            String s = txt1.getText();          // [CE]ボタンの処理
            int stt = txt1.getSelectionStart();
            int end = txt1.getSelectionEnd();
            if(stt> end) { int t = stt; stt = end; end = t; }
            if(stt==end && stt> 0){ // BS
                s = s.substring(0,stt-1)+s.substring(stt);
                txt1.setText(s);
                txt1.setCaretPosition(stt-1);
            }else if(stt< end){     // Cut
                txt1.cut();
            }
        } else if(e.getSource()==aBtn[Arrays.asList(sBtD).indexOf("Ans")]) {
            setStrings(Ans);                    // [Ans]ボタンの処理
        } else if(e.getSource()==aBtn[Arrays.asList(sBtD).indexOf("MC")]) {
            Mem = "0.0"; lbl1.setText("");      // [MC]ボタンの処理
        } else if(e.getSource()==aBtn[Arrays.asList(sBtD).indexOf("MR")]) {
            setStrings(Mem);                    // [MR]ボタンの処理
        } else if(e.getSource()==aBtn[Arrays.asList(sBtD).indexOf("MS")]) {
            Mem = txt2.getText();               // [MS]ボタンの処理
            if(Mem.equals("") || Mem.contains("error: ")) Mem = "0.0";
            if(Mem.equals("0.0")) lbl1.setText(""); else lbl1.setText("M");
        } else if(e.getSource()==aBtn[Arrays.asList(sBtD).indexOf("M+")]) {
            String s = txt2.getText();
            if(s.equals("") || s.contains("error: ")) s = "0.0";
            Mem = jsc.sEval(Mem+"+"+s);         // [M+]ボタンの処理
            if(Mem.equals("0.0")) lbl1.setText(""); else lbl1.setText("M");
        } else if(e.getSource()==aBtn[Arrays.asList(sBtD).indexOf("M−")]) {
            String s = txt2.getText();
            if(s.equals("") || s.contains("error: ")) s = "0.0";
            Mem = jsc.sEval(Mem+"-("+s+")");    // [M−]ボタンの処理
            if(Mem.equals("0.0")) lbl1.setText(""); else lbl1.setText("M");
        }
        for(int i = 0; i< nBtn; i++)
            if(e.getSource()==aBtn[i] && sBtO[i]!="")
                setStrings(sBtO[i]);
        txt1.requestFocus();
    }

    public void setStrings(String s) {
        int stt = txt1.getSelectionStart();
        int end = txt1.getSelectionEnd();
        int n = 0;
        if      (s.contains("()") ) n = 1;
        else if (s.contains("(,)")) n = 2;
        if (s=="Rnd()") n = 0;
        if (stt> end) { int t = stt; stt = end; end = t; }
        String t = txt1.getText();
        t=t.substring(0,stt)+s+t.substring(end);
        txt1.setText(t);
        txt1.setCaretPosition(stt+s.length()-n);
    }

    class MyKeyListener extends KeyAdapter {    // リスナークラス
        public void keyPressed(KeyEvent e){ 
            int k = e.getKeyCode();
            
            if       (k == KeyEvent.VK_DOWN) {
                iCnt++;
                if(iCnt> sHis.size()) iCnt = sHis.size();
                txt2.setText(""); txt1.setText(sHis.get(iCnt % sHis.size()));
            } else if(k == KeyEvent.VK_UP) {
                iCnt--;
                if(iCnt< 1) iCnt = 1;
                txt2.setText(""); txt1.setText(sHis.get(iCnt % sHis.size()));
            }
        }
    }

    class MyMouseAdapter extends MouseAdapter {
        public void mouseClicked(MouseEvent e) {
            // 右クリックが押されたかどうかの判定
            if(e.getModifiers()!=MouseEvent.BUTTON3_MASK) return;
            if      (e.getSource()==btn1)
                setStrings("=");
            else if (e.getSource()==btn2){
                txt1.setText(""); txt2.setText(""); // [C]
                Mem = "0.0"; lbl1.setText("");      // [MC]
            }
            txt1.requestFocus();
        }
    }
}

class JsCalc {
    ScriptEngineManager factory = new ScriptEngineManager();
    ScriptEngine engine = factory.getEngineByName("JavaScript");
    
    //--- コンストラクター ---//
    JsCalc() {
        defUserFunc();  // ユーザー定義関数の設定
    }
    // JavaでJavaScriptのeval()関数を呼び出す
    public String sEval(String sExpr) {
        String sScript = "with(Math){"+sExpr+"}";   // 「Math.」を省略可にするため
        try{
            return( engine.eval(sScript).toString() );
        } catch(Exception e) {
            return("error: "+e);
        }
    }
    // ユーザー定義関数の設定
    private void defUserFunc() {
        sEval("radians = function(x) {return(x*PI/180);}");
        sEval("degrees = function(x) {return(x/PI*180);}");
        sEval("sinDeg  = function(x) {return(sin(radians(x)));}");
        sEval("cosDeg  = function(x) {return(cos(radians(x)));}");
        sEval("tanDeg  = function(x) {return(tan(radians(x)));}");
        sEval("gcd     = function(a,b) {return((b == 0)? a : gcd(b,a%b));}");
        sEval("lcm     = function(a,b) {return(a*(b/gcd(a,b)));}");
        String sScript = "";
        sScript = "horner = function(coeff,x) {var r=0;"
                + "for(var i=0; i< coeff.length; i++){r=r*x+coeff[i];}"
                + "return(r);}";
        sEval(sScript);
        sScript = "sum = function(a) {var sum=0;"
                + "for(var i=0; i< a.length; i++) sum+=a[i];"
                + "return(sum);}";
        sEval(sScript);
        // 関数を追加(JsCalcW3から)
        sScript = "sign = function(x) {x=+x;"
                + "if(x===0||isNaN(x)){return x;}"
                + "return (x> 0)?1:-1;}";
        sEval(sScript);
        sEval("cbrt  = function(x) {return sign(x)*pow(abs(x),1/3);}");
        sEval("rcp   = function(x) {return 1/x;}");
        sEval("Rnd   = function()  {return random();}");
        sEval("sinh  = function(x) {return (exp(x)-exp(-x))/2;}");
        sEval("sinh  = function(x) {return (exp(x)-exp(-x))/2;}");
        sEval("cosh  = function(x) {return (exp(x)+exp(-x))/2;}");
        sScript = "tanh = function(x) {"
                + "if(abs(x)===Infinity){return sign(x);}"
                + "else{var y=exp(2*x);return (y-1)/(y+1);}}";
        sEval(sScript);
        sEval("asinh = function(x) {return(x===-Infinity)?x:log(x+sqrt(x*x+1));}");
        sEval("acosh = function(x) {return log(x+sqrt(x*x-1));}");
        sEval("atanh = function(x) {return log((1+x)/(1-x))/2;}");
        sEval("ln    = function(x) {return log(x);}");
        sEval("log10 = function(x) {return log(x)/LN10;}");
        sEval("ten   = function(x) {return pow(10,x);}");
        sEval("log_  = function(a,x) {return log(x)/log(a);}");
        // 関数を追加(JsCalcW5から)
        sEval("Rad   = function(x) {return radians(x);}");
        sEval("Deg   = function(x) {return degrees(x);}");
        sEval("fact  = function(x) {var r=1; for(var i=2; i<=min(x,171); r*=i++); return r;}");
        // 後はご自由に追加してください。 sEval("");
    }
}

●実行結果

※参考URL
http://stackoverflow.com/questions/3959211/fast-factorial-function-in-javascript
簡易関数電卓JsCalc - Java
簡易関数電卓JsCalcをGUI化してみた。JsCalcW - Java
簡易関数電卓JsCalcをGUI化してみた。JsCalcW - Java (2)
簡易関数電卓JsCalcをGUI化してみた。JsCalcW - Java (3)
簡易関数電卓JsCalcWを改良してみた。

すべての人のためのJavaプログラミング 第2版

すべての人のためのJavaプログラミング 第2版