Sep 4, 2012

Smart Calculator

Java Math
Calculator is one of the applications that every programmer want to create, it is fun to see how a simple code can make such an awesome arithmetic operations. People write calculator code in different ways, depending on the kind of language you are familiar with, and how easy or otherwise that language can avail you that, as well as how complex the operation would be. Java has a great library for UX and maths, which is great for developing calculator or other educational business/ applications.

 Smart Calculator was programmed in Java, featured both standard and scientific modes, with bunch of look and feels of the major platforms. I build those features upon the commonly seen plain open source calculators, providing an important programming techniques for dealing with Java's pluggable look and feels that are based on reflection. I used temp_value variables to store that values of previous operation, operator also points to the current operator to be used upon operands.ClcMode class defined the behavior of the side panel containing the scientific mode. InsetsPanel class add beauty to the layouts of our components, how the relate to one another.

First and foremost we declare and initialize the global variables, upon which the application functionality depended. I also created the dimensions the insets here.

public class calculator extends JFrame implements ActionListener
{
    

        JPanel mainPanel;
        JTextField field;
        private double temp_value1,temp_value2,end_value,flex;
        static double mem_1,mem_2;
        private int reserve=1,reserve2=0,reserve3=0,reserve4=0;
        private char operator;
        protected LookAndFeels slf;
        private JButton    one,two,three,four,five,six,seven,eight,
          nine,zero,cls,pow_of_2,pow_of_3,exp;

        private JButton plus,min,div,log,rec,prod,equl,pm,
          dec,mr,mc,mp,mm,sqrt,sin,cos,tan;
        JMenuBar bar;
        JMenu view;
        JMenu about;
        JMenuItem exit;
        JMenuItem dev;
        JRadioButtonMenuItem standard,scientific,win,metal;
        JSeparator sep_1,sep_2;
        ButtonGroup bg_1,bg_2;
        Container content_pane;
        JPanel text_panel,side_panel,button_panel;
        LookAndFeels lf;

        Vector lafContainer = new Vector();

        JComboBox jcb;
        JPanel comboPanel;
        
        //define the dimensions used in setting the layout insets 
        public final static Dimension hpad10 = new Dimension(10,1);
        public final static Dimension vpad20 = new Dimension(1,20);
        public final static Dimension vpad7 = new Dimension(1, 7);
        public final static Dimension vpad4 = new Dimension(1, 4);
        public final static Insets insets = new Insets(5, 5, 5,5);
       
        .........................

}
This is the application constructor where more importantly the initializations of look and feels, main user interface components takes place. we also need to register our text field here and also the release of the UI component on exit.
     
public calculator()
{
            init_lookAndFeel();
            
            register_main_components();
            init_ux_components();
            mainPanel.setLayout(new BorderLayout());
            mainPanel.add("Center", button_panel);
            mainPanel.add("North", text_panel);
            mainPanel.add("South",comboPanel);
            
            setLocation(300, 250);
            content_pane.add(mainPanel);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            field.addKeyListener(new KeyAdapter() {
                @Override
                public void keyTyped(KeyEvent keyevent) {
                    char key_char = keyevent.getKeyChar();
                    if (key_char >= '0' && key_char <= '9') {
                        //not handled here, so let the handler do the work
                    } else {
                        //other consume the action event
                        keyevent.consume();
                    }
               }
           });       
         
 }

The init_ux_components() method is responsible for the creation of the user interface components and their attachments to the panels. It also does the layouts and insets settings.
 private void init_ux_components() {
           text_panel.add(field);
           button_panel = new JPanel();

           button_panel.setLayout(new BoxLayout(button_panel,Box.WIDTH));
           button_panel.setBorder(
           BorderFactory.createTitledBorder("Smart Calculator"));

           button_panel.setLayout(new GridLayout(5, 4, 3, 3));
            
           side_panel = new InsetsPanel(insets);
           side_panel.setLayout(new GridLayout(5, 1, 3, 3));
            
           bar = new JMenuBar();
           view = new JMenu("View");
            
           standard = new JRadioButtonMenuItem("Standard", true);
           standard.setMnemonic('S');
           standard.addItemListener(new ClcMode());
            
           scientific = new JRadioButtonMenuItem("Sceintific");
           scientific.setMnemonic('c');
           scientific.addItemListener(new ClcMode());
            
           sep_1 = new JSeparator();
            
           win = new JRadioButtonMenuItem("Programmer");
           win.setMnemonic('w');
          
           metal = new JRadioButtonMenuItem("Statistics", true);
           metal.setMnemonic('m');
            
           sep_2 = new JSeparator();
            
           exit = new JMenuItem("Exit");
           exit.setMnemonic('E');
           exit.addActionListener(this);
            
           bg_1 = new ButtonGroup();
           bg_1.add(standard);
           bg_1.add(scientific);
           bg_2 = new ButtonGroup();
           bg_2.add(win);
           bg_2.add(metal);
            
           view.add(standard);
           view.add(scientific);
           view.add(sep_1);
           view.add(win);
           view.add(metal);
           view.add(sep_2);
           view.add(exit);

           bar.add(view);

           about = new JMenu("About Me");
           dev = new JMenuItem("Developer");
           dev.addActionListener(this);
            
           about.add(dev);
           bar.add(about);
           setJMenuBar(bar);

           mr = new JButton("MR");
           button_panel.add(mr);
           mr.addActionListener(this);

           /**Create the buttons, register them to event listener
              and add them to the their panel**/
           seven = new JButton("7");
           button_panel.add(seven);
           seven.addActionListener(this);
            
           eight = new JButton("8");           
           button_panel.add(eight);
           eight.addActionListener(this);
            
           nine = new JButton("9");
           button_panel.add(nine);
           nine.addActionListener(this);

          ..........................
            
 }
I decided to create and register the main component in a separate method for more readability. You may override the default Java frame icon, I used a screenshot of the Calculator here, or just skip line if you want the default logo.
  
private void register_main_components() {
          setIconImage(new ImageIcon(getClass()
               .getResource("calc.gif")).getImage());
          mainPanel = new InsetsPanel(insets);
          jcb = new JComboBox(lafContainer);
          jcb.addActionListener(this);
          jcb.setEditable(false);
            
          comboPanel = new JPanel();
          comboPanel.add(new JLabel("Look And Feels"));
          comboPanel.add(jcb);
            
          content_pane = getContentPane();
          content_pane.setLayout(new BorderLayout());
          text_panel = new InsetsPanel(insets);
          //textpanel.add(Box.createRigidArea(hpad7));
          text_panel.add(Box.createRigidArea(vpad7));
          Font font = new Font("Arial", Font.PLAIN, 18);
          field = new JTextField(25);
          field.setFont(font);
          field.setHorizontalAlignment(SwingConstants.RIGHT);
           
        
    }

For the look & feel, I decided to write a small API which would be referenced by the setup_laf() method. getSelectedItem() return the available look & feels installed on platform through UIManager class refference. updateComponentTreeUI() & updateComponentTreeUI() methods are necessary for the update of look & feels any time we made a new selection from the combobox. This happens through the SwingUtilities class reference.

 private void setup_laf() {
        LookAndFeels lfs = (LookAndFeels) jcb.getSelectedItem();
        LookAndFeel myLf = lfs.getLaf();
            try {
                UIManager.setLookAndFeel(myLf);
                SwingUtilities.updateComponentTreeUI(this);
                SwingUtilities.updateComponentTreeUI(side_panel);
            } catch (UnsupportedLookAndFeelException ex) {
                Logger.getLogger(calculator.class.getName())
                     .log(Level.SEVERE, null, ex);
            }
    }
Initialize the look and feel object, populating the items into the combo box list container

 private void init_lookAndFeel() {
          UIManager.LookAndFeelInfo[] info = UIManager.getInstalledLookAndFeels();
            for(UIManager.LookAndFeelInfo linfo : info){
            try {
                Class lafClass = Class.forName(linfo.getClassName());
                try {
                    LookAndFeel newLaf = (LookAndFeel) (lafClass.newInstance());
                    if(newLaf.isSupportedLookAndFeel()){
                        String name = linfo.getName();
                        lafContainer.add(new LookAndFeels(name,newLaf));
                        System.out.println(name);
                    }
                } catch (InstantiationException ex) {
                    continue;
                } catch (IllegalAccessException ex) {
                    continue;
                }
            } catch (ClassNotFoundException ex) {
                Logger.getLogger(calculator.class.getName())
                    .log(Level.SEVERE, null, ex);
            }
        }
    }

We need an inner class that builds the abstract radio button in the menu which we register to our radio button components earlier. It need to be validated immediately for the change to take effect.
class ClcMode implements ItemListener
   {
    public void itemStateChanged(ItemEvent event)
   {
        AbstractButton button=(AbstractButton)event.getItem();
        String label=button.getText();
        {
          if(label.equals("Standard"))
          {
                mainPanel.remove(side_panel);
                validate();
          }
        if(label.equals("Sceintific"))
          {
                mainPanel.add("West",side_panel);
                validate();
          }

      }
    }
  }

The actionPerformed() need to be implemented, as the class initially indicated that. in this method we take, process execute actions for every click and mathematical operations. We need to store every value we put inside field in a temp variable for later operation. The logic behind temp variable clear, we just want the best result for every calculation we made. You may figure out better way to compute store those values, but this method worked very well though.
 
public void actionPerformed(ActionEvent ae)
{
        //get the command label
        String cmd=ae.getActionCommand();
        //get the source action label
        Object src = ae.getSource();
        
        if(cmd.equals("Developer"))
        {
             open_dialog();
        }
       
        if(src.equals(jcb))
        {
           setup_laf();
        }
        if(cmd.equals("Exit"))
        {
           System.exit(0);
        }
        if(cmd.equals("1"))
        {
           if(reserve4==0)
           {
              field.setText(field.getText()+"1");
           }
           else
           {
              field.setText("");
              field.setText(field.getText()+"1");
              reserve4=0;
          }
        }
        if(cmd.equals("2"))
        {
            if(reserve4==0)
            {
               field.setText(field.getText()+"2");
            }
            else
            {
               field.setText("");
               field.setText(field.getText()+"2");
                reserve4=0;
            }
        }

         .............................

        if(cmd.equals("C"))
        {
           field.setText("");
           reserve2=0;
           reserve3=0;
           reserve4=0;
        }
        if(cmd.equals("log"))
        {
           if(field.getText().equals(""))
           {
                 field.setText("");
           }
           else
           {
                flex=Math.log(Double.parseDouble(field.getText()));
                field.setText("");
                field.setText(field.getText() + flex);
           }
        }
        if(cmd.equals("1/x"))
        {
            if(field.getText().equals(""))
            {
                 field.setText("");
            }
            else
            {
                  flex=1/Double.parseDouble(field.getText());
                  field.setText("");
                  field.setText(field.getText() + flex);
            }
        }
        if(cmd.equals("Exp"))
        {
            if(field.getText().equals(""))
            {
                 field.setText("");
            }
            else
            {
                 flex=Math.exp(Double.parseDouble(field.getText()));
                 field.setText("");
                 field.setText(field.getText() + flex);
            }
        }
        if(cmd.equals("x^2"))
        {
           if(field.getText().equals(""))
           {
                field.setText("");
           }
           else
           {
               flex=Math.pow(Double.parseDouble(field.getText()),2);
               field.setText("");
               field.setText(field.getText() + flex);
           }
        }
        if(cmd.equals("x^3"))
        {
           if(field.getText().equals(""))
           {
              field.setText("");
           }
           else
           {
              flex=Math.pow(Double.parseDouble(field.getText()),3);
              field.setText("");
              field.setText(field.getText() + flex);
           }
        }
        if(cmd.equals("+/-"))
        {
           if(reserve2==0)
           {
              field.setText("-"+field.getText());
              reserve2=1;
           }
           else
           {
              field.setText(field.getText());
           }
        }
        if(cmd.equals("."))
        {
           if(reserve3==0)
           {
              field.setText(field.getText()+".");
              reserve3=1;
           }
           else
           {
              field.setText(field.getText());
           }
        }
        if(cmd.equals("+"))
        {
           if(field.getText().equals(""))
           {
                field.setText("");
                temp_value1=0;
                operator='+';
           }
           else
           {
                temp_value1=Double.parseDouble(field.getText());
                field.setText("");
                operator='+';
                reserve3=0;
                reserve2=0;
           }

                field.requestFocus();
        }
        if(cmd.equals("-"))
        {
           if(field.getText().equals(""))
           {
                field.setText("");
                temp_value1=0;
                operator='-';
           }
           else
           {
                reserve2=0;
                reserve3=0;
                temp_value1=Double.parseDouble(field.getText());
                field.setText("");
                operator='-';
           }
           field.requestFocus();
        }
        if(cmd.equals("/"))
        {
           if(field.getText().equals(""))
           {
                field.setText("");
                temp_value1=1;
                operator='/';
           }
           else
           {
                reserve2=0;
                reserve3=0;
                temp_value1=Double.parseDouble(field.getText());
                operator='/';
                field.setText("");
           }
           field.requestFocus();
        }
        if(cmd.equals("*"))
        {
           if(field.getText().equals(""))
           {
                field.setText("");
                temp_value1=1;
                operator='*';
           }
           else
           {
                reserve2=0;
                reserve3=0;
                temp_value1=Double.parseDouble(field.getText());
                operator='*';
                field.setText("");
           }
           field.requestFocus();
        }
        if(cmd.equals("MC"))
        {
                mem_1=0;
                field.setText("");
        }
        if(cmd.equals("MR"))
        {
                field.setText("");
                field.setText(field.getText() + mem_1);
        }
        if(cmd.equals("M+"))
        {
                if(reserve==1)
                {
                   mem_1=Double.parseDouble(field.getText());
                   reserve++;
                }
                else
                {
                   mem_1+=Double.parseDouble(field.getText());
                   field.setText(""+mem_1);
                }
        }
        if(cmd.equals("M-"))
        {
                if(reserve==1)
                {
                   mem_1=Double.parseDouble(field.getText());
                   reserve++;
                }
                else
                {
                   mem_1-=Double.parseDouble(field.getText());
                   field.setText(""+mem_1);
                }
         }
         if(cmd.equals("√"))
         {
                if(field.getText().equals(""))
                {
                   field.setText("");
                }
                else
                {
                   flex=Math.sqrt(Double.parseDouble(field.getText()));
                   field.setText("");
                   field.setText(field.getText() + flex);
                }
         }
         if(cmd.equals("SIN"))
         {
                if(field.getText().equals(""))
                {
                   field.setText("");
                }
                else
                {
                   flex=Math.sin(Double.parseDouble(field.getText()));
                   field.setText("");
                   field.setText(field.getText() + flex);
                }
          }
          if(cmd.equals("COS"))
          {
                if(field.getText().equals(""))
                {
                    field.setText("")
               
                }
                else
                {
                   flex=Math.cos(Double.parseDouble(field.getText()));
                   field.setText("");
                   field.setText(field.getText() + flex);
                }
          }
          if(cmd.equals("TAN"))
          {
                if(field.getText().equals(""))
                {
                   field.setText("");
                }
                else
                {
                   flex=Math.tan(Double.parseDouble(field.getText()));
                   field.setText("");
                   field.setText(field.getText() + flex);
                }
         }
         if(cmd.equals("="))
         {
            if(field.getText().equals(""))
            {
                field.setText("");
            }
            else
            {
                temp_value2 = Double.parseDouble(field.getText());
                switch(operator)
                {
                     case '+':
                        end_value=temp_value1+temp_value2;
                     break;
                     case '-':
                        end_value=temp_value1-temp_value2;
                     break;
                     case '/':
                        end_value=temp_value1/temp_value2;
                    break;
                    case '*':
                        end_value=temp_value1*temp_value2;
                    break;
                }
                field.setText("");
                field.setText(field.getText() + end_value);
                reserve4=1;
               }
          }
       field.requestFocus();
 }
The entry point of the calculator. The frame size of "500,300" seems ok
 public static void main(String args[])
 {
            calculator n = new calculator();
            n.setTitle("SMART CALCULATOR");
            n.setSize(500,300);
            n.setResizable(false);
            n.setVisible(true);

}
We need this inner class to encapsulate our insets, make the code cleaner.
class InsetsPanel extends JPanel{
       Insets i;
       InsetsPanel(Insets i){
           this.i = i;
       }
       public Insets getInsets(){
           return i;
       }
   }
This is the result of the complete program

No comments:

Post a Comment