Page 1 of 1

A simple RPN Calculator Rate Topic: -----

#1 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8316
  • View blog
  • Posts: 31,836
  • Joined: 06-March 08

Posted 20 November 2010 - 09:24 PM

Since HP introduced the Reverse Polish Notation calculators in the mid 70s I use to used them.
I really don't understand why these calculators came unpopular as they are really oriented for programmer's mind.
My old HP16C is about to dye, I am ready to pay few hundred $ for a new one but HP does not make them anymore.
So I wrote my own RPN calculator last week. Here is it absolutly free for you.
It uses byte, word, integer, long or BigInteger. You can compute 12345678889986862 at power 23456789.
This is not really a tutorial but too much code to be post in the Code Snippet section.
There is a lot of comments in the code and I will be glad to answer any question about it.
Many classes have good example of Swing GUI features and how to use them.

For the JFrame a used my VariableGridPanel class already posted in the Code Snippet section

VariableGridPanel.java

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

/**
 * A class that implements a JPanel which is split in a grid like in a GridLayout
 * but a lot easier to use than a GridBagLayout
 * 
 * Differences with GridLayout:
 * - you can only add JComponent to it
 * - you are not force to add a component to all cells
 * - for each component you add you specify on whow many grid it should spread
 *   horizontaly and verticaly (default 1, 1) this allow a component to span over more than one cell
 * - you can add and remove component whenever you want
 * - a cell can be shared by two components (bigger than 1X1) they will just overlap
 * - when a gap is specified it also apply to the beginning and edge of the JPanel
 *  
 * I wrote it quick and dirty because I quickly need a calculator (usually ideal from GridLayout) 
 * but I need the "0" and "Enter" key to spread over 2 cells
 */
public class VariableGridPanel extends JPanel {

	private static final long serialVersionUID = 1L;
	
	// an ArrayList to hold all the JComponent added
	private ArrayList<CompPosSize> al = new ArrayList<CompPosSize>();
	// number of rows, columns and the gap in pixels between them
	private int nbRow, nbCol, xGap, yGap;
	
	/**
	 * Constructor that receives the number of row and column as parameter
	 */
	public VariableGridPanel(int row, int col) {
		// wich calls the constructor where gaps are secified
		this(row, col, 0, 0);
	}
	/**
	 * Constructor that receives the number of row, the number of columns and the gap
	 */
	public VariableGridPanel(int row, int col, int hGap, int vGap) {
		// we are using a null panel
		super(null);
		nbRow = row;
		nbCol = col;
		xGap = hGap;
		yGap = vGap;
	}
	
	// to add a JComponent with its coordinates
	public void addComp(JComponent comp, int row, int col) {
		// defaulted to 1 grid width and 1 grid height
		addComp(comp, row, col, 1, 1);
	}
	// to add a JComponent that spread over more than one cell
	public void addComp(JComponent comp, int row, int col, int w, int h) {
		// register the JComponent and its x,y,w,h in the ArrayList
		al.add(new CompPosSize(comp, row, col, w, h));
		add(comp);
	}
	
	// to remove a component
	public void remove(JComponent comp) {
        int size = al.size();
        // scan the arraylist
		for(int i = 0; i < size; i++) {
			CompPosSize cps = al.get(i);
			// if found
			if(cps.comp == comp) {
				super.remove(comp);
				al.remove(i);    // remove it
				return;			 // done
			}
		}
	}

	/**
	 * Overload the paintComponent method
	 */
	public void paint(Graphics g) {
		// before calling my super.paintComponent() method
		// determine the size of each registered component
		Dimension size = getSize();
		
		int cellWidth = size.width / nbCol;
		int cellHeight = size.height / nbRow;
		for(CompPosSize cps : al) {
			// the x position is x * cellWidth
			int xPos = cellWidth * cps.x + xGap;
			// the width is the cellWidth times my width in cell - gap
			int width = cellWidth * cps.w - xGap;
			// same thing on the yAxis
			int yPos = cellHeight * cps.y + yGap;
			int height = cellHeight * cps.h - yGap;
			// set the bounds of our component
			cps.comp.setBounds(xPos, yPos, width, height);
		}
		
		// call my super to paint the components now they have been positionned
		super.paint(g);
	}
	
	/*
	 * To Unit test the whole thing
	 */
	public static void main(String[] args) {
		// create a frame
		JFrame f = new JFrame("Test variable grid");
		// and a VariableGridPanel
		VariableGridPanel vgp = new VariableGridPanel(10, 10, 2, 1);
		// add different buttons for our test
		vgp.addComp(new JButton("BTN0"), 0, 0, 2, 1);
		vgp.addComp(new JButton("BTN1"), 2, 2);
		vgp.addComp(new JButton("BTN3"), 3, 1);
		vgp.addComp(new JButton("BTN4"), 3, 2);
		vgp.addComp(new JButton("BTN2"), 2, 5, 2, 3);
		// and one that will share a cell with the previous one
		vgp.addComp(new JButton("BTN5"), 4, 6, 2, 2);
		vgp.addComp(new JButton("BTN6"), 9, 9);
		// and a JLabel that I will make disappear
		JLabel label = new JLabel("I will eventually disappear :)/>");
		vgp.addComp(label, 6, 2, 4, 1);
		f.add(vgp);
		f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		// show the JFrame
		f.setSize(500, 500);
		f.setVisible(true);
		
		// wait 3 seconds
		try {
			Thread.sleep(3000);
		}
		catch(Exception e) {}
		// remove the JLabel
		vgp.remove(label);
		vgp.repaint();
	}
	
	/**
	 * A private class that holds a JComponent, its coordinates and size
	 */
	private class CompPosSize {
		JComponent comp;
		int x, y, w, h;
		
		/**
		 * Constructor
		 */
		CompPosSize(JComponent comp, int row, int col, int width, int height) {
			this.comp = comp;
			y = row;
			x = col;
			w = width;
			h = height;
		}
	}
}



The rightmost part of the calculator shows an Ascii table in a JScrollPane. You can scroll that pane to see ascii value.
The ascii value of the number in the led display is showed in YELLOW. A JCheckedBox in the main panel enable/disable the
feature of having the scroll pane to scroll to the displayed value. This is a good tutorial on how to have a JTable to

display a particular row.

import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.table.*;

import java.awt.*;

/*
 * To display the ascii characters set
 */
public class AsciiPanel extends JPanel {

	private static final long serialVersionUID = 1L;
	
	// the ascii representation of the first 32 control characters
	private static final String[] control = {"nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", "bsp", "tab",
		"lf", "vt", "np", "cr", "so", "si", "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", "can",
		"em", "eof", "esc", "fs", "gs", "rs", "us"};
	// the header of the JTable
	private static String[] header = {"Dec", "Hex", "Char"};
	
	private JTable table;
	private JViewport viewport;
	// last called (init to last one surely not showed at startup)
	private int lastCode = 255;
	// table model which implements CellRenderer
	private Model model;
	// not to create a TableModelEvent at each time the character changes
	// we will pre-create the 256 of them
	private TableModelEvent[] tme = new TableModelEvent[256];
	// the checkBox that says if we should scroll the AsciiPanel to show the character
	private JCheckBox checkBox;
	
	/*
	 * Constructor
	 */
	AsciiPanel(JCheckBox checkBox) {
		super(new BorderLayout());
		this.checkBox = checkBox;
		// header at the top in the North Region
		JLabel l = new JLabel("Ascii");
		l.setHorizontalAlignment(SwingConstants.CENTER);
		l.setOpaque(true);
		l.setBackground(Color.YELLOW);
		l.setForeground(Color.BLUE);
		add(l, BorderLayout.NORTH);
		
		// creation of the table
		model = new Model();
		table = new MyTable();
		JScrollPane scrollPane = new JScrollPane(table);
		viewport = scrollPane.getViewport();
		add(scrollPane, BorderLayout.CENTER);
		setBorder(BorderFactory.createLineBorder(Color.BLACK));
		
		// creates a model event for every row
		for(int i = 0; i < 255; ++i)
			tme[i] = new TableModelEvent(model, i);
	}
	
	// Assumes table is contained in a JScrollPane. Scrolls the
	// cell (rowIndex, vColIndex) so that it is visible within the viewport.
	private void scrollToVisible(int rowIndex, int vColIndex) {
	    // This rectangle is relative to the table where the
	    // northwest corner of cell (0,0) is always (0,0).
	    Rectangle rect = table.getCellRect(rowIndex, vColIndex, true);

	    // The location of the viewport relative to the table
	    Point pt = viewport.getViewPosition();

	    // Translate the cell location so that it is relative
	    // to the view, assuming the northwest corner of the
	    // view is (0,0)
	    rect.setLocation(rect.x -pt.x, rect.y -pt.y);

	    // Scroll the area into view
	    viewport.scrollRectToVisible(rect);
	}

	/*
	 * The method invoked by the DisplayPanel saying which Ascii code to show
	 */
	void showAscii(int code) {
		// if no change
		if(lastCode == code) {
			return;
		}
		// save last code to fired TableModelEvent if required
		table.tableChanged(tme[lastCode]);
		table.tableChanged(tme[code]);
		lastCode = code;
		// if checkbox is disabled exit
		if(!checkBox.isSelected())
			return;
		// else display the last code
		scrollToCode();
	}
	
	/*
	 * Scroll to JTable to show the code
	 */
	void scrollToCode() {
		// position the scrolling region (about 30 normally showed so make it as we
		// wanted to show row  @to do center the stuff
		scrollToVisible(lastCode, 0);
	}
	/*
	 * Extends JTable just to be able to set the cell renderer
	 */
	class MyTable extends JTable {

		private static final long serialVersionUID = 1L;
		// to save the model which is also the renderer
		
		MyTable() {
			super(model);
		}
		/** tell to call the Model which is also a renderer for all rendering (we return JLabel) */
		public TableCellRenderer getCellRenderer(int row, int col) {
			return model;
		}

	}
	/*
	 * The model for the JTable
	 */
	class Model extends AbstractTableModel implements TableCellRenderer {

		private static final long serialVersionUID = 1L;
		
		private JLabel label;
		// contructor
		Model() {
			// prepare the JLabel tha I will return
		    label = new JLabel();
		    label.setHorizontalAlignment(SwingConstants.CENTER);
		    label.setOpaque(true);
		    label.setForeground(Color.BLUE);		
		}

		// decimal, hexa and the ascii value
		public int getColumnCount() {
			return 3;
		}
        // the 256 asii code
		public int getRowCount() {
			return 256;
		}
        
		public String getColumnName(int col) {
			return header[col];
		}
		@Override
		// just return an object we have a cell renderer anyhow
		public Object getValueAt(int row, int col) {
			return "";
		}

		@Override
		public Component getTableCellRendererComponent(JTable arg0,
				Object arg1, boolean arg2, boolean arg3, int row, int col) {
		    if(row == lastCode)
		    	label.setBackground(Color.YELLOW);
		    else
		    	label.setBackground(Color.WHITE);
		    
		    // column 0 return the decimal value
			if(col == 0) {
				label.setText(String.format("%d", row));
			} else if(col == 1) {    // column 1 the hex value
				label.setText(String.format("x%X", row));
			} else  { // the ascii representation
				if(row < control.length) {
					label.setText("<" + control[row] + ">");
				}
				else {
					String str = Character.toString((char) row);
				    label.setText(str);
				}
			}
			
			return label;
		}
		
	}
}


The BinaryPanel shows the last 64 bits of the number displayed in the display.
It is a turorial about to change the Font size of a JComponent

import javax.swing.*;
import java.awt.*;
/*
 * This panel display the last 64 bits of the number displayed on the led of the RPN calculator in
 * the following format
 * 0000 0000 0000 0000 0000 0000 0000 0000
 *        56        48        40        32
 * 0000 0000 0000 0000 0000 0000 0000 0000
 *        24        16         8         0
 *        
 * Both the bits and the bits numbers are only showed based on the word size
 */
public class BinaryPanel extends VariableGridPanel {

	private static final long serialVersionUID = 1L;
	
	private static int[] bitNumberStr = {56, 48, 40, 32, 24, 16, 8, 0};
	
	// the JLabel for the bitNumber
	private JLabel[] bitNumber = new JLabel[8];
	// the jLabel for the 64 bits
	private JLabel[] label = new JLabel[64];
	/*
	 * Constructor
	 */
	BinaryPanel() {
		// 4 rows with 39 columns  8 times 4 bits plus a gap between them
		super(4, 39);
		setBackground(new Color(220, 220, 220));
		// smaller Font
		Font font = new JLabel().getFont();
		float size = font.getSize2D() * 0.9f;
		Font fontBit = font.deriveFont(size);
		size = font.getSize2D() * 0.8f;
		font = font.deriveFont(size);
		// the bit number
		int col = 0;        // starting column
		int row = 1;        // starting row
		for(int i = 0; i < bitNumber.length; i++) {
			// right justified using 8 bits + the space between the 2 quatuor 1111 1111
			bitNumber[i] = new JLabel(String.format("[%02d]", bitNumberStr[i]));
			bitNumber[i].setHorizontalAlignment(SwingConstants.RIGHT);	
			bitNumber[i].setForeground(Color.DARK_GRAY);
			bitNumber[i].setFont(font);
			// add to panel 9 cases width
			addComp(bitNumber[i], row, col, 9, 1);
			col += 10;
			// begin second line
			if(i == 3) {
				row = 3;
				col = 0;        // and restart a col 0
			}
		}
		// the bit labels put in reverse order
		col = 0;
		row = 0;
		for(int i = 63; i >= 0; i--) {
			label[i] = new JLabel("" + i);
			label[i].setHorizontalAlignment(SwingConstants.CENTER);
			label[i].setForeground(Color.BLUE);
			label[i].setFont(fontBit);
			addComp(label[i], row, col);
			++col;
			if(i % 4 == 0)
				++col;
			// change line a bit 32
			if(i == 32) {
				row = 2;
				col = 0;   // and restart from the beginning
			}
		}
		setBorder(BorderFactory.createLineBorder(Color.BLUE, 1));
		// init first time with 0 for int
		refresh(StackElement.SIZE_INT, "0");
	}
	
	// update from the main display
	protected void refresh(int wordSize, String bin) {
	
		// make the bit number visible 
		for(int i = 0; i < bitNumber.length - 1; ++i)
			bitNumber[i].setVisible(true);
		// now the bits
		// check if big integer
		int ilen = bin.length();
		if(ilen > 64) {
			// but we will keep the leading 0
			bin = bin.substring(ilen - 64);
			ilen = 64;
		}
		char[] bit = bin.toCharArray();
		
		// put to " " all bits in greater word size
		for(int i = ilen; i < 64; ++i)
			label[i].setText("");
		// put the set bits
        int index = ilen;
		for(int i = 0; i < ilen; ++i) {
			label[--index].setText("" + bit[i]);
		}
		// make visible the one that should be seen
		int max = 56;
		for(int i = 0; i < bitNumber.length; i++) {
			if(ilen > max)
				break;
			bitNumber[i].setVisible(false);
			max -= 8;
		}
			
			
	}
}


The ButtonCallBack interface was written because at design time I though that different class would be called back

depending of what button was pressed. It is actually not used as all call back when a button is pressed is done to the

MainDisplay class. Anyhow it is an example of how an Interface is implemented.
/**
 * This interface is used for the call back of a JButton
 * The RpnButton constructor receives as parameter a ButtonCallBack
 * They implement and actionPerformed and when this one is fired
 * they call buttonclick() with a code that was provided when the button was built
 *
 */
public interface ButtonCallBack {

	// button has been clicked and returned that code
	void buttonclick(int code);
}


CalculatorGUI is the main class. It is the one that creates the JFrame for the whole application. It is the one that

contains the main() method which is invoked to start the application.
When a button is clicked it performs what it has to do and then transfer back the focus to the JFrame.
That is not very elegant but it allows the JFrame to react to keyboard input as it always have focus.

import javax.swing.*;

import java.awt.*;
import java.awt.event.*;
/**
 * The RpnCalculator GUI
 */
public class CalculatorGUI extends JFrame implements KeyListener, ActionListener, MouseListener {

	private static final long serialVersionUID = 1L;

	private static final String copyRight = "Copyright © 2010 Les Services Informatiques PBL";
	private static final Color darkGreen = new Color(0, 110, 0);
	// the JLabel where to enter data
	private MainDisplay mainDisplay;
	// we have to save it for the calls back when the word size of the base change
	private StackPanel stackPanel;
	// the buttons for keyboard access
	private RpnButton rpnButton;
	// The AsciiPanel for call back when scrolling is enabled
	private AsciiPanel asciiPanel;
	// The checkbox if we scroll the ascii table and if we display the help
	private JCheckBox asciiCheckBox, helpCheckBox;
	// The label for context help
	private JLabel helpLabel;
	
	/**
	 * Constructor
	 */
	CalculatorGUI() {
		super("RPN Calculator");

		// First the panels that are independant from the others
		BinaryPanel binaryPanel = new BinaryPanel();
		MemoryPanel memoryPanel = new MemoryPanel(this);
			
		// the JCheckBox for ascii scrolling
		asciiCheckBox = new JCheckBox("Scroll Ascii Table");
		asciiCheckBox.addActionListener(this);
		asciiCheckBox.addMouseListener(this);
		// the checkBox for help display
		helpCheckBox = new JCheckBox("Display buttons help", true);
		helpCheckBox.addActionListener(this);
		helpCheckBox.addMouseListener(this);
		// the panel offering the RadioButtons and the checkedBoxes
		JPanel wordBasePanel = new WordSizeAndBasePanel(this, asciiCheckBox, helpCheckBox);

		// create the stack
		RpnStack stack = new RpnStack(this);
		// the stackPanel was built when the RpnStatck was built
		stackPanel = stack.getPanel();
	
		// the ascii panel
		asciiPanel  = new AsciiPanel(asciiCheckBox);
		
		// the main display that display the output led abd performs the opeartion
		mainDisplay = new MainDisplay(stack, binaryPanel, memoryPanel, asciiPanel);

		// generate the buttons
		rpnButton = new RpnButton(mainDisplay, this);
		// the Panel that display the Calculator buttons
		// needs to be called once the rpnButtons has been created
		CenterPanel centerPanel = new CenterPanel();		

		// the led display of the calculator
		JLabel label = mainDisplay.getLabel();
		label.setOpaque(true);
		label.setBackground(Color.WHITE);
		label.setHorizontalAlignment(SwingConstants.RIGHT);
		label.addMouseListener(this);
		// put in a ScrollPane if ever it becomes really big
		JScrollPane displayPane = new JScrollPane(label);
		displayPane.setBorder(BorderFactory.createLineBorder(Color.BLUE));
	
		// the JLabel to display Help
		helpLabel = new JLabel(copyRight);
		helpLabel.setOpaque(true);
		helpLabel.setBackground(Color.WHITE);
		helpLabel.addMouseListener(this);
		
		// position all our internal panels
		//---------------------------------
		// The panel we will put in the center containing all the others
		VariableGridPanel p = new VariableGridPanel(19, 5, 3, 3);

		// the main display at the top left
		p.addComp(displayPane, 0, 0, 3, 2);
		// put the memoryPanel in
		p.addComp(memoryPanel, 2, 0, 1, 3);
		// the panel that offer the RadioButtons for base and word size and the checkboxes on the left just under 

memory
		p.addComp(wordBasePanel, 5, 0, 1, 13);
		// the binary panel with the binary representation under the display
		p.addComp(binaryPanel, 2, 1, 2, 3);
		// the Panel that display the Calculator buttons just under it
		p.addComp(centerPanel, 5, 1, 2, 13);
		// the stack panel at the left
		p.addComp(stackPanel, 0, 3, 1, 18);
        // and the Ascii panel at its right
		p.addComp(asciiPanel, 0, 4, 1, 18);
		// the help
		p.addComp(helpLabel, 18, 0, 5, 1);
		
		// all that in the Center Region
		add(p, BorderLayout.CENTER);

		// for keyboard access
		this.setFocusable(true);
		addKeyListener(this);
	}

	// to display the help message
	protected void setHelpText(String str) {
		// if help display is enabled
		if(helpCheckBox.isSelected()) {		
			if(str == null) {
				helpLabel.setForeground(Color.BLACK);
				helpLabel.setText(copyRight);
			}
			else {
				helpLabel.setForeground(darkGreen);
				helpLabel.setText(str);
			}
		}
	}
	// change in the base inform everybody who should be notified about it
	protected void setBase(int baseIdx) {
		// enable/disable the buttons depending of the base
		RpnButton.setBase(baseIdx);
		// change the display in the stack
		stackPanel.setBase(baseIdx);
		mainDisplay.setBase(baseIdx);
	}
	// change in the word size inform everybody who should be notified about it
	protected void setWordSize(int wordSize) {
		mainDisplay.setWordSize(wordSize);
		stackPanel.setWordSize(wordSize);
	}

	// called by the StackPanel to set the register X to a certain value
	void setReg0(StackElement se) {
		// will be null if coming from MemoryPanel
		if(se == null) 
			mainDisplay.setReg0(null);
		else   // clone it before sending it in
			mainDisplay.setReg0(new StackElement(se));
	}
	/*
	 * A listener on the checkbox just to reset 
	 * the focus on the JFrame
	 */
	public void actionPerformed(ActionEvent e) {
		Object o = e.getSource();
		// keep the focus on the frame for the KeyListener
		if(o == asciiCheckBox) {
			asciiPanel.scrollToCode();
		}
		else {						// the help enabled/disable
			// if disabled put the copyright
			if(!helpCheckBox.isSelected()) {
				helpLabel.setForeground(Color.BLACK);
				helpLabel.setText(copyRight);
			}
		}
		this.requestFocus();
	}

	/* A key is entered on the keyboard. Thansfer to our RpnButton */
	public void keyPressed(KeyEvent e) {
		char c = e.getKeyChar();
		int code = e.getKeyCode();
		rpnButton.keyTyped(c, code);
	}

	/* For the Help of the CheckBox */
	public void mouseEntered(MouseEvent e) {
		Object o = e.getSource();
		if(o == asciiCheckBox)
			setHelpText("Check this button if you want the Ascii table to scroll automatically to the lowest 

byte value in the X register.");
		else if(o == helpCheckBox)
			setHelpText("Check this button to have a help text displayed at the bottom of the screen when your 

mouse is over a button.");
		else if(o == helpLabel)
			setHelpText("To disable multiple messages displayed in green here check off the \"Display buttons 

help\" CheckedBox at the left of the panel.");
		else   // so it has to be the master display
			setHelpText("The master register second operand of all operations. Named X in Help messages 

displayed");
	}

	// clear the help message
	public void mouseExited(MouseEvent e) {
		setHelpText(null);
	}
	public void keyTyped(KeyEvent e) {}
	public void keyReleased(KeyEvent arg0) {}
	public void mouseClicked(MouseEvent arg0) {}
	public void mousePressed(MouseEvent arg0) {}
	public void mouseReleased(MouseEvent arg0) {}

	/**
	 * To start the whole applicatio
	 */
	public static void main(String[] args) {

		// prepare the GUI to run in another thread
		Runnable detach = new Runnable() {
			public void run() {
				CalculatorGUI rpncGui= new CalculatorGUI();
				// make it visible and display it
				rpncGui.setBounds(100, 100, 750, 500);
				rpncGui.setVisible(true);
				rpncGui.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
			}
		};
		// start the thread
		SwingUtilities.invokeLater(detach);
	}
}


The CenterPanel class is the class that display the calculator buttons. It has an actionListener that calls back the

MainDisplay class.
public class CenterPanel extends VariableGridPanel {

	private static final long serialVersionUID = 1L;
	
	/**
	 * Constructor 
	 */
    CenterPanel() {
    	super(6, 7, 2, 2);
    	
       	addComp(RpnButton.getButton(RpnButton.BTN_FLIP), 0, 0);
       	addComp(RpnButton.getButton(RpnButton.BTN_POW), 0, 1);
       	addComp(RpnButton.getButton(RpnButton.BTN_A), 0, 2);
           	
    	addComp(RpnButton.getButton(RpnButton.BTN_CLEAR), 1, 0);
    	addComp(RpnButton.getButton(RpnButton.BTN_ABS), 1, 1);
    	addComp(RpnButton.getButton(RpnButton.BTN_B)/>, 1, 2);
    	addComp(RpnButton.getButton(RpnButton.BTN_BSP), 1, 3);
    	addComp(RpnButton.getButton(RpnButton.BTN_POP), 1, 4);
    	addComp(RpnButton.getButton(RpnButton.BTN_C), 2, 2);
    	addComp(RpnButton.getButton(RpnButton.BTN_D), 3, 2);
    	addComp(RpnButton.getButton(RpnButton.BTN_E), 4, 2);
    	addComp(RpnButton.getButton(RpnButton.BTN_F), 5, 2);
    	
       	addComp(RpnButton.getButton(RpnButton.BTN_SEVEN), 2, 3);
       	addComp(RpnButton.getButton(RpnButton.BTN_HEIGHT), 2, 4);
       	addComp(RpnButton.getButton(RpnButton.BTN_NINE), 2, 5);
       	addComp(RpnButton.getButton(RpnButton.BTN_MULT), 2, 6);
       	
       	addComp(RpnButton.getButton(RpnButton.BTN_FOUR), 3, 3);
       	addComp(RpnButton.getButton(RpnButton.BTN_FIVE), 3, 4);
       	addComp(RpnButton.getButton(RpnButton.BTN_SIX), 3, 5);
       	addComp(RpnButton.getButton(RpnButton.BTN_MINUS), 3, 6);
       	
       	addComp(RpnButton.getButton(RpnButton.BTN_ONE), 4, 3);
       	addComp(RpnButton.getButton(RpnButton.BTN_TWO), 4, 4);
       	addComp(RpnButton.getButton(RpnButton.BTN_THREE), 4, 5);
       	addComp(RpnButton.getButton(RpnButton.BTN_PLUS), 4, 6);
       	
       	addComp(RpnButton.getButton(RpnButton.BTN_ZERO), 5, 3, 2, 1);
       	addComp(RpnButton.getButton(RpnButton.BTN_ENTER), 5, 5, 2, 1);
       	
       	addComp(RpnButton.getButton(RpnButton.BTN_ROT_L), 2, 0);
       	addComp(RpnButton.getButton(RpnButton.BTN_ROT_R), 2, 1);
       	addComp(RpnButton.getButton(RpnButton.BTN_SHIFT_L), 3, 0);
       	addComp(RpnButton.getButton(RpnButton.BTN_SHIFT_R), 3, 1);
       	addComp(RpnButton.getButton(RpnButton.BTN_OR), 4, 0);
       	addComp(RpnButton.getButton(RpnButton.BTN_XOR), 4, 1);
       	addComp(RpnButton.getButton(RpnButton.BTN_AND), 5, 0);
       	addComp(RpnButton.getButton(RpnButton.BTN_NOT), 5, 1);
  
       	addComp(RpnButton.getButton(RpnButton.BTN_MEM_SET), 0, 4);
       	addComp(RpnButton.getButton(RpnButton.BTN_MEM_RECALL), 0, 5);
       	addComp(RpnButton.getButton(RpnButton.BTN_RMD), 0, 6);
       	
       	addComp(RpnButton.getButton(RpnButton.BTN_CHANGE_SIGN), 1, 5);
       	addComp(RpnButton.getButton(RpnButton.BTN_DIV), 1, 6);
              	
     }    
}


The MainDisplay class is the one that performs all mathematical/logical operations. It reacts to button push because the

CenterPanel defined it as ActionListener to all buttons.

import javax.swing.*;

import java.awt.*;
import java.math.BigInteger;

/**
 * The main display where user enters value
 *
 */
public class MainDisplay {

	// the basic digit stuff
	private static String displayCode = "0123456789ABCDEF"; 

	// the baseIdx to use default to Decimal at construction time
	private int baseIdx = StackElement.DECIMAL_IDX;
	// the translation in base
	private static final int[] base = {16, 10, 8, 2};
	// the max size in hex and bin and octal
	private static final int[] hexMaxSize = {0, 16, 8, 4, 2};
	private static final int[] binMaxSize = {0, 64, 32, 16, 8};
	// for the max and min in decimal
	private static final BigInteger bigMax = new BigInteger("" + Long.MAX_VALUE);
	private static final BigInteger bigMin = new BigInteger("" + Long.MIN_VALUE);
	
	// the panel that display MemoryContent
	private MemoryPanel memoryPanel;
	
	// the word size
	private int wordSize = StackElement.SIZE_INT;

	// the StackElement that I display
	private StackElement display = new StackElement("0", baseIdx, StackElement.SIZE_INT);
	// end the memory
	private StackElement memory = new StackElement("0", baseIdx, StackElement.SIZE_INT);

	// the JLabel to display user's number in the base
	private JLabel label;
	// the actual number to be display the JLabel 
	private String numberStr;
	// if the lastOparation was "enter". In that case do not push new display
	private boolean lastOperationWasEnter;
	// if last entered was a digit
	private boolean lastOperationWasDigitInput = true;

	// the stack
	private RpnStack stack;
	// the second display
	private SecondDisplay secondDisplayPanel;
	// the AsciiPanel
	private AsciiPanel asciiPanel;

	/**
	 * Constructor
	 */
	protected MainDisplay(RpnStack stack, SecondDisplay secondDisplayPanel, MemoryPanel memoryPanel, AsciiPanel asciiPanel) {

		this.stack = stack;
		this.secondDisplayPanel = secondDisplayPanel;
		this.memoryPanel = memoryPanel;
		this.asciiPanel = asciiPanel;
		// the label that display the number
		numberStr = "0";

		// the label at the top that display based on the base
		label = new JLabel(numberStr + " ");

		// increase the size of the digit
		Font font = label.getFont();
		label.setFont(font.deriveFont(font.getSize2D() * 1.4f));
		// init the first ascii showed
		asciiPanel.showAscii(0);
	}

	// call back when a button is clicked
	public void buttonclick(int code) {

		// if it is a digit button we call the method that appends
		if(code <= RpnButton.BTN_F) {
			userTyped(code);
			return;
		}
		// so it is not a digit
		lastOperationWasDigitInput = false;    // will be for all but backspace  and +/- that will switch it later
		// big switch for the other
		switch(code) {

		// enter on: we simply have to push on the stack a copy of the display
		case RpnButton.BTN_ENTER:
			StackElement se = new StackElement(display);
			stack.push(se);
			lastOperationWasEnter = true;
			updateDisplay();
			break;
			
		// a pop
		case RpnButton.BTN_POP:
			display = stack.pop();
			numberStr = display.toString(wordSize, baseIdx);
			updateDisplay();
			break;
			

		// backspace as been entered
		case RpnButton.BTN_BSP:
			// exception a backspace and +/- are like digit input
			lastOperationWasDigitInput = true;
			processBSP();
			break;

		// a change sign
		case RpnButton.BTN_CHANGE_SIGN:
			// exception a backspace and +/- are like digit input
			lastOperationWasDigitInput = true;
			processChangeSign();
			break;

		// + - * /
		case RpnButton.BTN_DIV:
		case RpnButton.BTN_PLUS:
		case RpnButton.BTN_MINUS:
		case RpnButton.BTN_MULT:
		case RpnButton.BTN_POW:
		case RpnButton.BTN_RMD:
		// logical
		case RpnButton.BTN_OR:
		case RpnButton.BTN_XOR:
		case RpnButton.BTN_AND:			
		case RpnButton.BTN_AND_NOT:
		case RpnButton.BTN_FLIP:
		case RpnButton.BTN_CLEAR:
		// shift
		case RpnButton.BTN_SHIFT_L:
		case RpnButton.BTN_SHIFT_R:
			processOperationCode(code);
			break;
		
		// no statck access
		case RpnButton.BTN_NOT:
		case RpnButton.BTN_ABS:
			performNoPopOperation(code);
			break;
			
		case RpnButton.BTN_ROT_L:
		case RpnButton.BTN_ROT_R:
			performRotateOperation(code);
			break;
		
		// memory operation
		case RpnButton.BTN_MEM_SET:
		case RpnButton.BTN_MEM_RECALL:
			performMemOperation(code);
			break;
		}
		
	}

	// user has type a digit to append to the display
	private void userTyped(int code) {
		// if the last operation was not a dgitEnterered save
		// the value on the stack but for enter
		if(!lastOperationWasDigitInput) {
			if(!lastOperationWasEnter) {
				StackElement last = new StackElement(numberStr, baseIdx, wordSize);
				stack.push(last);				
			}
			numberStr = "";
		}
		lastOperationWasDigitInput = true;
		lastOperationWasEnter = false;
		// length of the display number in BigInteger
		int ilen = numberStr.length();
		// get ride of the leading "0" if there is one
		if(ilen == 1 && numberStr.charAt(0) == '0') {
			numberStr = "";
			// reset the fact that the last operation was an "enter"
		}
		// reset the String value in BigInteger
		numberStr += displayCode.charAt(code);
		updateDisplay();
	}

	/*
	 * Update label display based on base and word size
	 */
	private void updateDisplay() {
		// reset our value based on that String
		display.setValue(numberStr, baseIdx, wordSize);	
		// get the value for that type based on base
		label.setText(display.toString(wordSize, baseIdx) + " ");
		asciiPanel.showAscii(display.getInt() & 0xFF);
		// display the second displays (using long as max)
		int tmpSize = wordSize;
		if(tmpSize == StackElement.SIZE_BIG)
			tmpSize = StackElement.SIZE_LONG;
		// get longValue for decimal panel
		long longValue = display.getLong();
		secondDisplayPanel.refresh(wordSize, 
				display.toString(tmpSize, StackElement.BINARY_IDX),
				display.toString(tmpSize, StackElement.HEXADECIMAL_IDX),
				longValue);
		// reset by default the digit buttons enabled or not
		RpnButton.setBase(baseIdx);
		// enable/disable the / and Reminder buttons if divisor == 0
		boolean divEnabled = true;
		if(numberStr.length() == 1 && numberStr.charAt(0) == '0') 
			divEnabled = false;
		RpnButton.getButton(RpnButton.BTN_DIV).setEnabled(divEnabled);
		RpnButton.getButton(RpnButton.BTN_RMD).setEnabled(divEnabled);
		RpnButton.getButton(RpnButton.BTN_BSP).setEnabled(divEnabled);
		
		// if last operation was a push does not disable some buttons
		if(lastOperationWasEnter)
			return;
		
		// there are no buttons to enable disable for BigInteger
		// all math operations are permitted and we can append digit to it
		if(wordSize == StackElement.SIZE_BIG)
			return;
		
		int ilen = numberStr.length();
		// in hexa and binary easy the number of digits entered let us konw if we can continue
		if(baseIdx == StackElement.HEXADECIMAL_IDX) {
			if(ilen < hexMaxSize[wordSize])
				return;
			disableBut(0);
			return;
		}
		if(baseIdx == StackElement.BINARY_IDX) {
			if(ilen < binMaxSize[wordSize])
				return;
			disableBut(0);
			return;
		}
		// in octal, we have to see if the binary representation of this octal number has room for 3 bits
		if(baseIdx == StackElement.OCTAL_IDX) {
			// get binary representation
			String str = display.toString(wordSize, StackElement.BINARY_IDX);
			if(str.length() > binMaxSize[wordSize] - 3)
				disableBut(0);
			return;
		}
		
		// we will use the long value to perform our tests
		// as Java promotes byte and short operation so int anyway
		long val = display.getLong();
		// determine which button should be disabled as they would overload the word size
		if(val == 0)   // 0 nothing to do 
			return;
		
		// Get the min and max value for that word size
		long max = StackElement.getMaxValue(wordSize);
		long min = StackElement.getMinValue(wordSize);
		// for byte, short, int and long you can always change a positive number
		// to its negative value.  This not the case for MIN_BYTE, MIN_SHORT,....
		// for example byte go from -128 to 127 and short from -32768 to 32767. 
		// If the value is the minimum value for the type we have to disable the +/- button
		if(val == min) 
			RpnButton.getButton(RpnButton.BTN_CHANGE_SIGN).setEnabled(false);
		// if one of any end disable all
		if(val == min || val == max) {
			disableBut(0);
			return;			
		}
		// for byte, short, int I  can test in a long
		// for long I will have to use a BigInteger
		// test first in within 10 times the size
		if(val > min / 10 && val < max / 10) {
			return;
		}
		
		// number that we can add to the (actual value * 10)
		long free;
		// we will have to use BigInteger
		if(wordSize == StackElement.SIZE_LONG) {
			// create BigInteger 10 times as big as our number
			BigInteger bigVal = new BigInteger(numberStr + "0");
			BigInteger bigFree;
			// positive
			if(val > 0) {
				if(bigVal.compareTo(bigMax) > 0) {
					disableBut(0);
					return;
				}
				bigFree = bigMax.subtract(bigVal);
			}
			else {						// negative
				if(bigVal.compareTo(bigMin) < 0) {
					disableBut(0);
					return;
				}
				bigFree = bigMax.subtract(bigVal);
				bigFree = bigFree.negate();
			}
			free = bigFree.longValue();
		}
		else {
			// for smaller than long I can perform the calculation in long
			// see if we can multiply by 10 to add another digit
			long times10 = val * 10;
			if(times10 < min || times10 > max) {
				disableBut(0);							// disable all buttons
				return;
			}
			// see if all digits are available
			// positive value
			if(val > 0) {
				free = max - times10;      // how many can I add after I multiply by 10
			}
			else {
				free = min - times10;
				free = -free;				// make it positive for single testing
			}
		}
		// so disable everything higer
		if(free < 9)
			disableBut((int) free + 1);
	}
	
	/*
	 * Disable buttons from N to F
	 */
	private void disableBut(int from) {
		for(int i = from; i < base[baseIdx]; i++)
			RpnButton.setOutOfRange(i);		
	}
	/*
	 * Change in the base
	 */
	void setBase(int baseIdx) {
		this.baseIdx = baseIdx;
		numberStr = display.toString(wordSize, baseIdx);
		updateDisplay();
		String str = memory.toString(wordSize, baseIdx);
		memoryPanel.setText(str);
	}
	/*
	 * Change in the word size
	 */
	void setWordSize(int wordSize) {
		this.wordSize = wordSize;
		numberStr = display.toString(wordSize, baseIdx);
		updateDisplay();
		String str = memory.toString(wordSize, baseIdx);
		memoryPanel.setText(str);
	}

	/*
	 * Call from the JFrame which was called by the StackPanel or the MemoryPanel so set the Reg0
	 * to a certain stack value
	 */
	protected void setReg0(StackElement se) {
		// if se == null it is for the memory register
		if(se == null) {
			performMemOperation(RpnButton.BTN_MEM_RECALL);
			return;
		}
		// OK coming from StackPanel
		display = se;
		numberStr = display.toString(wordSize, baseIdx);
		updateDisplay();
	}
	/*
	 * For the CenterPanel to access my label and pane
	 */
	JLabel getLabel() {
		return label;
	}

	// handles a BackSpace
	private void processBSP() {
		// user decided to keep that number
		lastOperationWasEnter = false;
		// if len == 1 then just put a 0
		int ilen = numberStr.length();
		if(ilen == 1) {
			numberStr = "0";
			updateDisplay();
			return;
		}
		// if len == 2 but first one is '-'
		if(ilen == 2 && numberStr.charAt(0) == '-') {
			numberStr = "0";
			updateDisplay();
			return;					
		}
		// remove last digit
		numberStr = numberStr.substring(0, ilen-1);
		updateDisplay();
	}

	// process the +/-
	private void processChangeSign() {
		// we are in decimal because +/- is disabled in other modes
		if(numberStr.length() == 1 && numberStr.charAt(0) == '0') 
			return;

		// reset the fact that the last operation was an "enter"
		lastOperationWasEnter = false;
		// check if first digit is a -
		if(numberStr.charAt(0) == '-')
			numberStr = numberStr.substring(1);
		else
			numberStr = "-" + numberStr;
		// redo calculations
		updateDisplay();		
	}
	
	// operations without pop
	private void performNoPopOperation(int code) {
		BigInteger val = display.getBigInteger();
	    BigInteger result = null;
	    switch(code) {
			case RpnButton.BTN_NOT:
				result = val.not();
				break;
			case RpnButton.BTN_ABS:
				result = val.abs();
				break;
	    }
		numberStr = result.toString(base[baseIdx]);
		// update our value	
		updateDisplay();
		
	}
	// memory clear/set/recall
	private void performMemOperation(int code) {
		
		switch(code) {
			case RpnButton.BTN_MEM_SET:
				memory = new StackElement(display);
				break;
			case RpnButton.BTN_MEM_RECALL:
				display = new StackElement(memory);
				lastOperationWasEnter = false;
				numberStr = display.toString(wordSize, baseIdx);
				updateDisplay();
				return;            // <--- return no need to update memory display
	    }
		// for clear and set update mem display
		String str = memory.toString(wordSize, baseIdx);
		memoryPanel.setText(str);
	}
	
	// process operations in big integer
	private void processOperationCode(int code) {
		
		BigInteger opr = stack.pop().getBigInteger();
		BigInteger val = display.getBigInteger();
	    BigInteger result = null;
		// as for a push/enter next character entered will clear display
		// no need to disable out of range digit
		switch(code) {
			case RpnButton.BTN_PLUS:
				result = opr.add(val);
				break;
			case RpnButton.BTN_MINUS:
				result = opr.subtract(val);
				break;
			case RpnButton.BTN_MULT:
				result = opr.multiply(val);
				break;
			case RpnButton.BTN_DIV:
				result = opr.divide(val);
				break;
			case RpnButton.BTN_OR:
				result = opr.or(val);
				break;
			case RpnButton.BTN_XOR:
				result = opr.xor(val);
				break;
			case RpnButton.BTN_AND:
				result = opr.and(val);
				break;
			case RpnButton.BTN_AND_NOT:
				result = val.not();
				result = result.and(opr);
				break;
			case RpnButton.BTN_POW:
				result = opr.pow(display.getInt());
				break;
			case RpnButton.BTN_SHIFT_L:
				result = opr.shiftLeft(display.getInt());
				break;
			case RpnButton.BTN_SHIFT_R:
				result = opr.shiftRight(display.getInt());
				break;
			case RpnButton.BTN_RMD:
				result = opr.remainder(val);
				break;
			case RpnButton.BTN_CLEAR:
			    result = opr.clearBit(display.getInt());
			    break;
			case RpnButton.BTN_FLIP:
			    result = opr.flipBit(display.getInt());
			    break;
			    
		}
		numberStr = result.toString(base[baseIdx]);
		// update our value	
		updateDisplay();
	}
	
	// rotate can only performed on long
	private void performRotateOperation(int code) {
		// the number that will be rotate
		StackElement num = stack.pop();
		// local variable for 
		int i, shift;
		String str;
		// as for a push/enter next character entered will clear display
		// no need to disable out of range digit
		
		switch(wordSize) {
			case StackElement.SIZE_LONG:
				long l;
				if(code == RpnButton.BTN_ROT_L)
					l = Long.rotateLeft(num.getLong(), display.getInt());
				else
					l = Long.rotateRight(num.getLong(), display.getInt());
				str = String.format("%d", l);
				display.setValue(str, StackElement.DECIMAL_IDX, StackElement.SIZE_LONG);
				break;
					
			case StackElement.SIZE_INT:
				if(code == RpnButton.BTN_ROT_L)
					i = Integer.rotateLeft(num.getInt(), display.getInt());
				else
					i = Integer.rotateRight(num.getInt(), display.getInt());
				str = String.format("%d", i);
				display.setValue(str, StackElement.DECIMAL_IDX, StackElement.SIZE_INT);
				break;
	
			case StackElement.SIZE_SHORT:
				i = num.getShort();
				// remove signe extension
				i &= 0xFFFF;
				shift = i << 16;
				// copy into higher bit
				i |= shift;
				if(code == RpnButton.BTN_ROT_L)
					i = Integer.rotateLeft(i, display.getInt());
				else
					i = Integer.rotateRight(i, display.getInt());
				i &= 0xFFFF;
				str = String.format("%d", i);
				display.setValue(str, StackElement.DECIMAL_IDX, StackElement.SIZE_SHORT);
				break;
				
			case StackElement.SIZE_BYTE:
				i = num.getByte();
				// remove sign extension
				i &= 0xFF;            // 000x
				shift = i << 8;       // 00x0
				shift |= i;           // 00xx
				shift <<= 8;          // 0xx0
				shift |= i;           // 0xxx
				shift <<= 8;          // xxx0
				shift |= i;           // xxxx
				i = shift;
				if(code == RpnButton.BTN_ROT_L)
					i = Integer.rotateLeft(i, display.getInt());
				else
					i = Integer.rotateRight(i, display.getInt());
				i &= 0xFF;
				str = String.format("%d", i);
				display.setValue(str, StackElement.DECIMAL_IDX, StackElement.SIZE_BYTE);
				break;
		}
		
		// have it back to actual representation
		numberStr = display.toString(wordSize, baseIdx);
		updateDisplay();
	}
}


The MemoryPanel displays the memory used to save a value. The calculator has a memory register to which any value can be

saved and later back recall.

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/*
 * Display the memory word
 */
public class MemoryPanel extends JPanel implements MouseListener {
	
	private static final long serialVersionUID = 1L;

	private JLabel displayLabel;
	private CalculatorGUI father;
	
	MemoryPanel(CalculatorGUI father) {
		super(new BorderLayout());
		this.father = father;
		setBorder(BorderFactory.createLineBorder(Color.BLACK));
		
		// the header
		JLabel label = new JLabel("Memory");
		label.setHorizontalAlignment(SwingConstants.CENTER);
		add(label, BorderLayout.NORTH);
	
		// to display word stored in memory
		displayLabel = new JLabel("0 ");
		displayLabel.setOpaque(true);
		displayLabel.setBackground(Color.WHITE);
		displayLabel.setForeground(Color.BLUE);
		displayLabel.setHorizontalAlignment(SwingConstants.RIGHT);
		displayLabel.addMouseListener(this);
		add(new JScrollPane(displayLabel), BorderLayout.CENTER);
		
		// add a border by putting 2 empty label east and west
		add(new JLabel("  "), BorderLayout.EAST);
		add(new JLabel("  "), BorderLayout.WEST);
		add(new JLabel("  "), BorderLayout.SOUTH);
	}
	
	/*
	 * To update memory display
	 */
	void setText(String str) {
		displayLabel.setText(str + " ");
	}

    // call main frame to update main display with memory register
	public void mouseClicked(MouseEvent arg0) {
		father.setReg0(null);
	}
	public void mouseEntered(MouseEvent arg0) {
		father.setHelpText("Click on this Memory register to copy it into the X register.");
	}
	public void mouseExited(MouseEvent arg0) {
		father.setHelpText(null);
	}
	public void mousePressed(MouseEvent arg0) {}
	public void mouseReleased(MouseEvent arg0) {}
}


The RpnButton class contains the definition of all buttons including the way they should be displayed in the GUI and the

help text associated to them. There is a nice tutorial on how to use a Swing.Timer and a MouseListener. When the mouse

enters a JButton the color of the button changes. When the mouse leaves, this color fades out due to successive Timer

calls.

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

/*
 * A class that extends JButton to expands there capabilities
 * - degrading of color when mouse leaves it
 * - disabled when related to a base they are irrelevant
 * - all the buttons are made static an registered in a static array so they can be accessed by multiple classes
 */
public class RpnButton  {


	// list of all the codes put the number first we will use their code value
	public static final int BTN_ZERO = 0, 
							BTN_ONE = 1, 
							BTN_TWO = 2, 
							BTN_THREE = 3, 
							BTN_FOUR = 4,
							BTN_FIVE = 5, 
							BTN_SIX = 6, 
							BTN_SEVEN = 7, 
							BTN_HEIGHT = 8, 
							BTN_NINE = 9,
							BTN_A = 10, 
							BTN_B = 11, 
							BTN_C = 12, 
							BTN_D = 13,  
							BTN_E = 14, 
							BTN_F = 15,
							BTN_ROT_L = 16, 
							BTN_ROT_R = 17, 
							BTN_SHIFT_L = 18, 
							BTN_SHIFT_R = 19,
							BTN_OR = 20, 
							BTN_XOR = 21, 
							BTN_NOT = 22, 
							BTN_AND = 23,
							BTN_CLEAR = 24, 
							BTN_MEM_SET = 25, 
							BTN_MEM_RECALL = 26, 
							BTN_CHANGE_SIGN = 27,
							BTN_DIV = 28, 
							BTN_MULT = 29, 
							BTN_PLUS = 30, 
							BTN_MINUS = 31, 
							BTN_ENTER = 32,
							BTN_BSP = 33, 
							BTN_POP = 34, 
							BTN_POW = 35, 
							BTN_RMD = 36, 
							BTN_ABS = 37,
							BTN_FLIP = 38; 

	// and their associated Strings. One for the button one for the Help
	private static String[][] label = {
		{"0", "Binary/Octal/Decimal/Hexadecimal 0."}, 
		{"1", "Binary/Octal/Decimal/Hexadecimal 1."}, 
		{"2", "Octal/Decimal/Hexadecimal 2 not available in Binary mode."}, 
		{"3", "Octal/Decimal/Hexadecimal 3 not available in Binary mode."}, 
		{"4", "Octal/Decimal/Hexadecimal 4 not available in Binary mode."}, 
		{"5", "Octal/Decimal/Hexadecimal 5 not available in Binary mode."}, 
		{"6", "Octal/Decimal/Hexadecimal 6 not available in Binary mode."}, 
		{"7", "Octal/Decimal/Hexadecimal 7 not available in Binary mode."}, 
		{"8", "Decimal/Hexadecimal 8 not available in Binary or Octal mode."}, 
		{"9", "Decimal/Hexadecimal 9 not available in Binary or Octal mode."},
		{"A", "Hexadecimal 0xA not enabled in other mode than Hexadecimal mode."}, 
		{"B", "Hexadecimal 0xB not enabled in other mode than Hexadecimal mode."}, 
		{"C", "Hexadecimal 0xC not enabled in other mode than Hexadecimal mode."}, 
		{"D", "Hexadecimal 0xD not enabled in other mode than Hexadecimal mode."}, 
		{"E", "Hexadecimal 0xE not enabled in other mode than Hexadecimal mode."}, 
		{"F", "Hexadecimal 0xF not enabled in other mode than Hexadecimal mode."},
		{"RoL", "Rotate Left the Y register by the number of bits specified in the X register. Stack is popped. 

(Register X Long word size used)."}, 
		{"RoR", "Rotate Right the Y register by the number of bits specified in the X register. Stack is popped. 

(Register X Long word size used)."}, 
		{"LsH", "Shift Left the Y register by the number of bits specified in the X register. (Register X Integer 

word size used)."}, 
		{"RsH", "Shift Right the Y register by the number of bits specified in the X register. (Register X Integer 

word size used)."}, 
		{"Or", "ORs the X and Y registers. Stack is popped."}, 
		{"Xor", "XORs the X and Y registers. Stack is popped."}, 
		{"Not", "Performs a logival NOT of the X register. The stack is NOT popped."}, 
		{"And", "ANDs the X and Y registers. Stack is popped."},		
		{"Clr", "Clears in Y register the bit specified in X register. Stack is popped. (Register X Integer word 

size used)."},
		{"MS", "Copy the X register into Memory register. (No stack operation)."}, 
		{"MR", "Copy the Memory register into X register. (No stack operation)."}, 
		{"±", "Changes the sign of the X register. Stack is NOT popped. (Only available in Decimal mode)."}, 
		{"/", "Divides Y register by the X register. Stack is popped. (Disabled when X register equals 0)."}, 
		{"*", "Multiplies Y register by the X register. Stack is popped."}, 
		{"+", "Adds Y register to the X register. Stack is popped."}, 
		{"-", "Subtracts the X register from the Y register. Stack is popped."}, 
		{"Enter", "Pushes the X register into the stack. Next digit entered would clear the X register. Backspace 

and ChangeSign would keep it."}, 
		{"BSP", "Removes the rightmost digit from the X register. (Not available when X register equals 0)."}, 
		{"Pop", "Stack is popped which copies the Y register into the X register."}, 
		{"POW", "Raise the Y register to the power of X register. Stack is popped. (Register X Integer word size 

used)."}, 
		{"RMD", "Performs the modulo of register Y by register X. Stack is popped. (Disabled when X register equals 

0)."}, 
		{"ABS", "Set the X register to absolute value. Stack is NOT popped. (Only available in Decimal mode)."},
		{"Flip", "Flips in Y register the bit specified in X register. Stack is popped. (Register X Integer word 

size used)."}, 
		};

	// the buttons kept in static variable 
	private static Btn[] button = new Btn[label.length];
	// an arraylist to contains all my buttons so when the base changes I can check if they 
	// should be enabled or not
	private static ArrayList<Btn> al = new ArrayList<Btn>(16);

	// a larger Font for the smaller one
	private Font smallFont, largerFont;	
	// the colors for my fadeout
	private Color[] color;
	// the color for the unary operator (do not change the stack)
	private static final Color unaryOprColor = new Color(0, 100, 0);       // dark greeen
	// the operators using that color
	private static final int unaryOpr[] = {BTN_CHANGE_SIGN, BTN_NOT, BTN_BSP, BTN_ABS, BTN_MEM_SET, BTN_MEM_RECALL};
	// the color for the operations that use the Long version of the X register
	private static final Color longColor = new Color(255, 0, 255);
	// the buttons that use X register long value
	private static final int longX[] = {BTN_ROT_L, BTN_ROT_R};
	// the color for the operations that use the Integer version of the X register
	private static final Color intColor = new Color(125, 0, 255);
	// the buttons that use X register long value
	private static final int intX[] = {BTN_SHIFT_L, BTN_SHIFT_R, BTN_CLEAR, BTN_FLIP, BTN_POW};
	// the margin
	private Insets inset = new Insets(1, 1, 1, 1);
	// the interface for which I will call the buttonclick method when I am clicked
	private ButtonCallBack callBack;
    // the JFrame for focus
	private CalculatorGUI master;

	RpnButton(ButtonCallBack callBack, CalculatorGUI master) {
		this.callBack = callBack;
		this.master = master;

		// build of yellow fading out
		// and the base and the button
		color = new Color[6];
		// start by full white
		int b = 255;
		// loop inversed removing 5 times 30 to b so 255 to 155
		color[5] = new Color(255, 255, B)/>;
		for(int i = 4; i >= 0; --i) {
			b -= 30;
			color[i] = new Color(255, 255, B)/>;
		}	

		// build the larger font and the font for "enter:
		JButton but = new JButton("");
		Font regularFont = but.getFont();
		float size = regularFont.getSize2D();
		smallFont = regularFont.deriveFont(size * 0.8f);
		largerFont = regularFont.deriveFont(size * 1.4f);
		// now generate the button
		for(int i = 0; i < button.length; i++)
			button[i] = new Btn(i);
	}

	/**
	 * To get a button
	 */
	static JButton getButton(int code) {
		return button[code];
	}

	// the base was changed
	protected static void setBase(int idx) {
		// enable/disable button accordingly
		for(Btn b : al) {
			// change sign is an exception
			if(b.code == BTN_CHANGE_SIGN || b.code == BTN_ABS)
				b.setEnabled(idx == StackElement.DECIMAL_IDX);
			else  // the other based on the base
				b.setEnabled(b.baseIdx >= idx);
		}
	}
	
	// disable button outOfRange
	protected static void setOutOfRange(int n) {
		button[n].setOutOfRange();
	}

	// a key was typed on the keyboard
	protected void keyTyped(char c, int keyCode) {
		int idx;
		if(c >= '0' && c <= '9')
			idx = c - '0';
		else if(c >= 'A' && c <= 'F')
			idx = c - 'A' + 10;
		else if(c >= 'a' && c <= 'f')
			idx = c - 'a' + 10;
		else if(c == '+')
			idx = BTN_PLUS;
		else if(c == '-')
			idx = BTN_MINUS;
		else if(c == '*')
			idx = BTN_MULT;
		else if(c == '/')
			idx = BTN_DIV;
		else if(keyCode == KeyEvent.VK_ENTER)
			idx = BTN_ENTER;
		else if(keyCode == KeyEvent.VK_BACK_SPACE || keyCode == KeyEvent.VK_DELETE)
			idx = BTN_BSP;
		else if(keyCode == KeyEvent.VK_DOWN)
			idx = BTN_POP;
		else if(keyCode == KeyEvent.VK_UP)
			idx = BTN_ENTER;
		else 
			return;

		// test if enabled
		if(button[idx].isEnabled()) {
			// fake a mouse entering over that but button
			button[idx].mouseEntered(null);
			// if it is not an out of value
			if(!button[idx].outOfRange) {
				// do like a regular call back
				callBack.buttonclick(idx);
			}
			// fake a mouse eciting event to start the shading
			button[idx].mouseExited(null);
		}
		
	}
	class Btn extends JButton implements ActionListener, MouseListener{
		// base index over which I am disable
		private static final long serialVersionUID = 1L;
		// code that I will send to the callBack routine
		private int code;
		// if the base of the GUI is higher than this value, I will be disabled
		private int baseIdx;
		// the Timer to fadeout my yellow color
		private Timer timer;
		// color index formy fadeout
		private int colorIdx;
		// if I am disabled because out of range for the word size
		// will be still enable and react to button click but won't perform call back
		private boolean outOfRange;
		
		/**
		 * Constructor for buttons that are always displayed
		 */
		private Btn(int code) {
			super(label[code][0]);
			// save code
			this.code = code;
			// if just a char make it larger
			if(label[code][0].length() == 1)
				setFont(largerFont);
			else if(code == BTN_ENTER)
				setFont(largerFont);
			setMargin(inset);
			setHorizontalAlignment(SwingConstants.CENTER);
			setBackground(Color.WHITE);			
			setForeground(Color.BLUE);
			// check for unary operator green
			for(int i = 0; i < unaryOpr.length; i++) {
				if(code == unaryOpr[i]) {
					setForeground(unaryOprColor);
					break;
				}
			}
			// long only
			for(int i = 0; i < longX.length; i++) {
				if(code == longX[i]) {
					setForeground(longColor);
					break;
				}
			}
			// int only
			for(int i = 0; i < intX.length; i++) {
				if(code == intX[i]) {
					setForeground(intColor);
					break;
				}
			}
			// my timer when I fade out my color
			timer = new Timer(100, this);
			// action listener when I click the button
			addActionListener(this);
			// to trap the mouse in and out and change my color accordingly
			addMouseListener(this);
			// the / button is disabled at start time (cannot divide by 0)
			if(code == BTN_DIV)
				setEnabled(false);
			// excption we register the change sign and ABS button only available in decimal
			if(code == BTN_CHANGE_SIGN || code == BTN_ABS) {
				al.add(this);
				return;
			}
			// if not a digit button (but 0 and 1) always enabled but we have to put them
			// because they can be disabled by the MainDisplay to avoid word overlap
			if(code < BTN_TWO || code > BTN_F) 
				baseIdx = StackElement.BINARY_IDX;
			// determine base
			else if(code < BTN_HEIGHT) 
				baseIdx = StackElement.OCTAL_IDX;
			else if(code > BTN_NINE)
				baseIdx = StackElement.HEXADECIMAL_IDX;
			else  // only 8 and 9 left
				baseIdx = StackElement.DECIMAL_IDX;
			al.add(this);
			// enable only decimal at start up
			if(baseIdx == StackElement.HEXADECIMAL_IDX)
				setEnabled(false);
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			Object o = e.getSource();
			// button was click execute my callback
			if(o == this) {
				if(outOfRange) {
					master.requestFocus();
					return;
				}
				callBack.buttonclick(code);
				master.requestFocus();
				return;
			}
			// ok it is the timer
			++colorIdx;
			if(colorIdx >= color.length) {
				timer.stop();
				return;
			}
			setBackground(color[colorIdx]);
		}

		/*
		 * Declare the button to large for word size
		 */
		private void setOutOfRange() {
			outOfRange = true;
			setFont(smallFont);
		}
		/*
		 * oveload regular steEnabled to stop color shading if in progress
		 */
		@Override
		public void setEnabled(boolean state) {
			// remove out of range if I am called is to enable or disable the button
			if(outOfRange) {
				outOfRange = false;
				setFont(largerFont);
			}
			// if enabling
			if(!state) {
				// if I am fading out
				if(timer.isRunning())
					timer.stop();
				setBackground(Color.WHITE);
			}
			super.setEnabled(state);
		}
		@Override
		public void mouseEntered(MouseEvent arg0) {
			// check if this button has tooltip (enabled or not)
			if(label[code].length == 2)
				master.setHelpText(label[code][1]);
			// do nothing is the button is disabled
			if(!isEnabled())
				return;
			// stop the timer if it was on
			timer.stop();
			// set the color to yellow
			setBackground(color[0]);
		}

		@Override
		public void mouseExited(MouseEvent arg0) {
			master.setHelpText(null);
			// do nothing is the button is disabled
			if(!isEnabled())
				return;
			// reset the color for fading out
			colorIdx = 0;
			timer.start();
		}


		public void mouseClicked(MouseEvent arg0) {}
		public void mousePressed(MouseEvent arg0) {}
		public void mouseReleased(MouseEvent arg0) {}
	}
}


An RPN calculator cannot exist without a Stack. This class contains an ArrayList of StackElement (what an original name :))
The stack contains a minimum of SIZE elements. If a Pop oreation would make the stack < SIZE a zero elemnt is added to it

import java.util.*;


/*
 * Holds the 5 RPN stack in 5 ArrayList (one for each word size) and keep them synchronized 
 */
public class RpnStack {

	// number of element to keep int the stack
	private static final int SIZE = 10;
	
	// an element at 0 use you fill it at init time and whatever all the SIZE slots are not used
	// (the base parameter is really not important here)
	private static final StackElement ZERO = new StackElement("0", StackElement.DECIMAL_IDX, StackElement.SIZE_INT);
	
	// the arrayList that contain our Stack
	private ArrayList<StackElement> al;
	// the panel that display my data
	private StackPanel panel;
	
	// constructor
	protected RpnStack(CalculatorGUI father) {
		// build the ArrayList with it's size
		al = new ArrayList<StackElement>(SIZE);
		// fill it with elements initialize to 0
		for(int i = 0; i < SIZE; ++i)
			al.add(ZERO);
		// build the panel to display my data
		panel = new StackPanel(this, father);
	}
	
	// to retreive the first element of the stack
	protected final StackElement pop() {
		StackElement se = al.remove(0);
		// if less size less than SIZE we pop too much so add a new one at 0
		if(al.size() < SIZE)
			al.add(ZERO);
		// on refresh
		panel.refresh();
		return se;
	}
	
	// to push a StackElement on the stack all the other ones are moved one up
	protected void push(StackElement se) {
		al.add(0, se);
		panel.refresh();
	}
	
	// returns an element
	protected StackElement get(int i) {
		return al.get(i);
	}
	// returns the size of the stack
	protected int getSize() {
		return SIZE;
	}
	
	// for the Gui to display my panel
	StackPanel getPanel() {
		return panel;
	}
}


The StackElement class defines the elements contained in the RpnStack. The class contains the BigInteger, long, int, word,

byte version of any number used in the calculator. It has a toString() method to return the displayable version of its

value base on the word size and base used.

import java.math.BigInteger;

/** 
 * Contains the 5 elements that are kept in the stack
 */
public class StackElement {
	
	static public final int HEXADECIMAL_IDX = 0, DECIMAL_IDX = 1, OCTAL_IDX = 2, BINARY_IDX = 3;
	static public final int SIZE_BIG = 0, SIZE_LONG = 1, SIZE_INT = 2, SIZE_SHORT = 3, SIZE_BYTE = 4; 
	private static final int baseList[] = {16, 10, 8, 2};
	
	// format because iInteger.toString, val, base) returns a minus sign - for Hex and Octal
	private static final String[] format = {"%X", "%d", "%o"};
	
	// the smallest and largest in long does not apply to BigInteger 
	private static final long min[] = {0, Long.MIN_VALUE, Integer.MIN_VALUE, Short.MIN_VALUE, Byte.MIN_VALUE};
	private static final long max[] = {0, Long.MAX_VALUE, Integer.MAX_VALUE, Short.MAX_VALUE, Byte.MAX_VALUE};
	
	// for octal conversion
	static private final char[] ascii = {'0', '1', '2', '3', '4', '5', '6', '7'};
	// the element in the 5 base
	private BigInteger bigValue;
	private long longValue;
	private int intValue;
	private short shortValue;
	private byte byteValue;
	
	// constructor receives a String (most often from the JLabel) and the actual base and wordSize
	protected StackElement(String label, int base, int wordSize) {
		setValue(label, base, wordSize);
	}
	
	// to clone a StackElement
	protected StackElement (StackElement se) {
		bigValue = new BigInteger(se.bigValue.toString());
		longValue = se.longValue;
		intValue = se.intValue;
		shortValue = se.shortValue;
		byteValue = se.byteValue;
	}
	
	// when changing a value or called by the constructor 
	protected void setValue(String label, int base, int wordSize) {
//		System.out.println("SetValue Str: >" + label + "< base: " + base + ">" + baseList[base]);
		// parse it anyhow as a BigInteger to avoid java expecting - sign for Hex/oct/bin
		bigValue = new BigInteger(label, baseList[base]);
		
		// convert from bigger to smaller
        longValue = bigValue.longValue();
		intValue = (int) longValue;
		shortValue = (short) intValue;
		byteValue = (byte) shortValue;
		
		// then promote from smaller to bigger
		switch(wordSize) {
			case SIZE_BYTE:
				longValue = intValue = shortValue = byteValue;
				break;
			case SIZE_SHORT:
				longValue = intValue = shortValue;
				break;
			case SIZE_INT:
				longValue = intValue;
				break;
		}	
//		System.out.println("> " + label + "< L " + longValue + " I " + intValue + " S " + shortValue + " B " + 

byteValue);
	}
	
	// returns the String representation based on size and base
	protected String toString(int sizeIdx, int baseIdx) {
        // for Short and Byte the hex string of intValue
		String str;
		// and it's len
		int ilen;
		// index of first not 0 when in binary
		int index;
		
		// depending of wordSize
		switch(sizeIdx) {
			case SIZE_BIG:
				str = bigValue.toString(baseList[baseIdx]);
				if(baseIdx == HEXADECIMAL_IDX)
					return str.toUpperCase();
				return str;
	            
			case SIZE_LONG:
				if(baseIdx == BINARY_IDX) 
					return Long.toBinaryString(longValue);						
				else
					return String.format(format[baseIdx], longValue);					

	

			case SIZE_INT:
				if(baseIdx == BINARY_IDX) 
					return Integer.toBinaryString(intValue);						
				else
					return String.format(format[baseIdx], intValue);					

	
				
			case SIZE_SHORT:
				switch(baseIdx) {
					case HEXADECIMAL_IDX:
						str = String.format("%X", intValue);
						ilen = str.length();
						if(ilen > 4)
							str = str.substring(ilen - 4);
						return str;
					case DECIMAL_IDX:
						return String.format("%d", shortValue);
					case OCTAL_IDX:
						return shortToOctalString();
					case BINARY_IDX: 
						str = Integer.toBinaryString(intValue & 0xFFFF);
						// get rid of leadind 0
						index = str.indexOf('1');
						if(index == -1)
							return "0";
						else
							return str.substring(index);
				}
				
			case SIZE_BYTE:
				switch(baseIdx) {
					case HEXADECIMAL_IDX:
						str = String.format("%X", intValue);
						ilen = str.length();
						if(ilen > 2)
							str = str.substring(ilen - 2);
						return str;
					case DECIMAL_IDX:
						return String.format("%d", byteValue);
					case OCTAL_IDX:
						return byteToOctalString();
					case BINARY_IDX: 
						str = Integer.toBinaryString(intValue & 0xFF);					
						// get rid of leadind 0
						index = str.indexOf('1');
						if(index == -1)
							return "0";
						else
							return str.substring(index);
				}

		} // end switch(sizeIdx)
		throw new IllegalStateException("Shouldn't pass here");
	}
	
	// return min and max value for a size
	static long getMinValue(int sizeIdx) {
		return min[sizeIdx];
	}
	// return min and max value for a size
	static long getMaxValue(int sizeIdx) {
		return max[sizeIdx];
	}

    /**
     * the getters
     */
	protected final BigInteger getBigInteger() {
		return bigValue;
	}
	protected final long getLong() {
		return longValue;
	}
	protected final int getInt() {
		return intValue;
	}
	protected final short getShort() {
		return shortValue;
	}
	protected final byte getByte() {
		return byteValue;
	}
	
	/*
	 * Octal conversion more complicated than the others because not on byte boundary
	 * we cannot i & 0xFF or i & 0xFFFF as we did for binaty or hexa
	 */
	private String byteToOctalString() {
		// to stor the octal digit as we found them
		char[] digit = new char[3];
		// index to digit[] as we go backward
		int digitIdx = digit.length;
		// in int (would be promoted anyway)
		int value = intValue;
		// to store twice the 3 first bits and the 2 last one
		int j;
		
		// the 2 first 3 bits
		for(int i = 0; i < 2; i++) {
			j = value & 0x7;
			digit[--digitIdx] = ascii[j];
			value >>= 3;
		}
		// the last 2 bits
		j = value & 0x3;
		digit[0] = ascii[j];
		
		return new String(digit);
	}
	private String shortToOctalString() {
		// to stor the octal digit as we found them
		char[] digit = new char[6];
		// index to digit[] as we go backward
		int digitIdx = digit.length;
		// in int (would be promoted anyway)
		int value = intValue;
		// to store 5 times the 3 first bits and the 2 last one
		int j;
		
		// the 2 first 3 bits
		for(int i = 0; i < 5; i++) {
			j = value & 0x7;
			digit[--digitIdx] = ascii[j];
			value >>= 3;
		}
		// the last single bit
		j = value & 0x1;
		digit[0] = ascii[j];
		
		return new String(digit);
	}
}


The StackPanel class displays the stack on the EAST region of the JFrame.
It has a MouseListener to display Help and to react to Mouse click. When a stackelement is clicked it's value is copied

into the X register. Regular RpnCalculators do not have this feature :)


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

/**
 * To display the stack
 *
 */
public class StackPanel extends JPanel implements MouseListener {

	private static final long serialVersionUID = 1L;
	
	// color of the label
	private static final Color lightColor = new Color(255, 255, 160);
	
	// to know which stack to display
	private int baseIdx = StackElement.DECIMAL_IDX;
	// the word size
	private int sizeIdx = StackElement.SIZE_INT;

	// who to call back for the values
	private RpnStack rpnStack;
	// the JLabel displaying the stack
	private JLabel[] label;
	// the master frame for help callback
	private CalculatorGUI father;

	/**
	 * Constructor
	 */
	protected StackPanel(RpnStack rpnStack, CalculatorGUI father) {
		
		super(new BorderLayout());
		this.father = father;
        setBorder(BorderFactory.createLineBorder(Color.BLACK));
		// save my stack
		this.rpnStack = rpnStack;
		int size = rpnStack.getSize();

		// a Header in the NorthRegion
		JLabel head = new JLabel("Stack");
		head.setHorizontalAlignment(SwingConstants.CENTER);
		head.setOpaque(true);
		head.setBackground(Color.YELLOW);
		add(head, BorderLayout.NORTH);
		
		// on the EastRegion the stack depth
		JPanel layoutPanel = new JPanel(new GridLayout(size, 1));
		// the labels of the depth
		for(int i = size - 1; i >= 0; --i) {
			JLabel label = new JLabel();
			// we call the first stack element Y
			if(i == 0) {
				label.setText(" Y ");
			}
			else {
				label.setText(" " + i + " ");
			}
			label.setBorder(BorderFactory.createLineBorder(Color.BLUE));
			label.setOpaque(true);
			label.setBackground(lightColor);
			label.setHorizontalAlignment(SwingConstants.CENTER);
			layoutPanel.add(label);
		}
		// added to the East Region
		add(layoutPanel, BorderLayout.WEST);

		// The center column
		layoutPanel = new JPanel(new GridLayout(size, 1));
		// the labels to display stack element
		label = new JLabel[size];
		for(int i = size-1; i >= 0; i--) {
			label[i] = new JLabel();
			label[i].setOpaque(true);
			label[i].setBackground(Color.WHITE);
			label[i].setHorizontalAlignment(SwingConstants.RIGHT);
			// for the help text or the click
			label[i].addMouseListener(this);
			JScrollPane sp = new JScrollPane(label[i]);
			sp.setBorder(BorderFactory.createLineBorder(Color.BLACK));
			layoutPanel.add(sp);
		}
		// add it center
		add(layoutPanel, BorderLayout.CENTER);	
		// load the stack for the first time
		refresh();
	}

	/**
	 * The base change
	 */
	protected void setBase(int idx)  {
		baseIdx = idx;
		// base changed the display element surely
		refresh();
	}
	/**
	 * The word size change
	 */
	protected void setWordSize(int idx)  {
		sizeIdx = idx;
		// base changed the display element surely
		refresh();
	}

	/*
	 * Refresh required, the stack changed
	 */
	protected void refresh() {
		// we update our labels
		for(int i = 0; i < label.length; ++i) {
			// the stack element to display
			StackElement se = rpnStack.get(i);
            // according to word size and base
			label[i].setText(se.toString(sizeIdx, baseIdx) + " ");
		}
	}


	// righ now just one JLabel has a mouse listener
	public void mouseEntered(MouseEvent e) {
		if(e.getSource() == label[0])
			father.setHelpText("The bottom of the stack which is the first operand of all operations performed. 

Named register Y in the help messages.");
		else
			father.setHelpText("Click on this stack register to copy it into the X register");
	}

	// simply erase the help text
	public void mouseExited(MouseEvent arg0) {
		father.setHelpText(null);
	}
	// mouse click we have to copy into register X but for label[0]
	public void mouseClicked(MouseEvent e) {
		Object o = e.getSource();
		for(int i = 0; i < label.length; i++) {
			if(o == label[i]) {
				// the stack element to display
				father.setReg0(rpnStack.get(i));
				return;
			}
		}
	}

	// unused mouse event
	public void mousePressed(MouseEvent arg0) {}
	public void mouseReleased(MouseEvent arg0) {}
}



The WordSizeAndBasePanel class is a JPanel containing the JRadioButton to select the word size and the base (Hexadecimal,

Decimal, Octal, Binary) to use. It also contains the JCheckedBox if the help should be displayed and if the AsciiPanel

should scroll to display the ascii value of the rightmost byte in the X register.

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


/**
 * Display the panel that permit to select the base and the size of the word
 *
 */
public class WordSizeAndBasePanel extends JPanel implements ActionListener {

	private static final long serialVersionUID = 1L;

	static private final String[] btnTextBase = {"Hexadecimal", "Decimal", "Octal", "Binary"};
	static private final String[] btnTextSize = {"BigInteger", "Long", "Integer", "Short", "Byte"};

	private JRadioButton[] radioBase = new JRadioButton[btnTextBase.length];
	private JRadioButton[] radioSize = new JRadioButton[btnTextSize.length];
	
	// the father that I will call when the base changes
	private CalculatorGUI rpn;
	/**
	 * Constructor
	 */
	WordSizeAndBasePanel(CalculatorGUI rpn, JCheckBox scroll, JCheckBox help) {
		// nb rows: 3 header + 2 JCheckBox + all JRadioButton
		super(new GridLayout(3 + 2 + btnTextBase.length + btnTextSize.length, 1, 3, 3));
		this.rpn = rpn;
		setBorder(BorderFactory.createLineBorder(Color.BLACK));

		// the base header
		JLabel label = new JLabel("--- Number Base ---");
		label.setHorizontalAlignment(SwingConstants.CENTER);
		label.setForeground(Color.BLUE);
		add(label);
		// the radioButton for the base
		ButtonGroup bg = new ButtonGroup();
		for(int i = 0; i < radioBase.length; i++) {
			radioBase[i] = new JRadioButton(btnTextBase[i]);
			radioBase[i].addActionListener(this);
			bg.add(radioBase[i]);
			add(radioBase[i]);
		}
		// set the decimal button to on
		radioBase[StackElement.DECIMAL_IDX].setSelected(true);	

		// the wordsize
		label = new JLabel("--- Word Size ---");
		label.setHorizontalAlignment(SwingConstants.CENTER);
		label.setForeground(Color.BLUE);
		add(label);
		// the radioButton for the word size
		bg = new ButtonGroup();
		for(int i = 0; i < radioSize.length; i++) {
			radioSize[i] = new JRadioButton(btnTextSize[i]);
			radioSize[i].addActionListener(this);
			bg.add(radioSize[i]);
			add(radioSize[i]);
		}
		// set the int button to on
		radioSize[StackElement.SIZE_INT].setSelected(true);	

		// the options
		label = new JLabel("--- Options ---");
		label.setHorizontalAlignment(SwingConstants.CENTER);
		label.setForeground(Color.BLUE);
		add(label);
		// the 2 checkbox
		add(scroll);
		add(help);
		
	}
	@Override
	public void actionPerformed(ActionEvent e) {
		// find wich button has been clicked
		Object but = e.getSource();
		for(int i = 0; i < radioBase.length; i++) {
			if(but == radioBase[i]) {
				rpn.setBase(i);      // inform everybody the base changed
				// give back focus to JFrame
				rpn.requestFocus();
				return;
			}
		}
		for(int i = 0; i < radioSize.length; i++) {
			if(but == radioSize[i]) {
				rpn.setWordSize(i);      // inform everybody the base changed
				// give back focus to JFrame
				rpn.requestFocus();
				return;
			}
		}
	}
	
}


This post has been edited by pbl: 20 December 2010 - 10:05 PM


Is This A Good Question/Topic? 1
  • +

Replies To: A simple RPN Calculator

#2 LynnL  Icon User is offline

  • D.I.C Head

Reputation: 21
  • View blog
  • Posts: 109
  • Joined: 13-April 09

Posted 21 November 2010 - 09:24 PM

PBL despite your respect, the Memory Clear button is missing
Was This Post Helpful? 0
  • +
  • -

#3 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8316
  • View blog
  • Posts: 31,836
  • Joined: 06-March 08

Posted 21 November 2010 - 09:49 PM

View PostLynnL, on 21 November 2010 - 10:24 PM, said:

PBL despite your respect, the Memory Clear button is missing

LynnL, despite your respect you do not need a MemoryClear in a RPN calculator
If your really wnt to clear memory, because you don't understabd how it works, just
0
enter
menSet
pop
:)
Was This Post Helpful? 1
  • +
  • -

#4 NeoTifa  Icon User is offline

  • Whorediot
  • member icon





Reputation: 2498
  • View blog
  • Posts: 15,465
  • Joined: 24-September 08

Posted 22 November 2010 - 02:57 PM

Peebles, honey, darling! Beautiful I must say, but could you explian the advantages/disadvantages of a rpn calculuator versus a regular standard calculator?
Was This Post Helpful? 0
  • +
  • -

#5 LynnL  Icon User is offline

  • D.I.C Head

Reputation: 21
  • View blog
  • Posts: 109
  • Joined: 13-April 09

Posted 23 November 2010 - 10:27 PM

View Postpbl, on 21 November 2010 - 08:49 PM, said:

View PostLynnL, on 21 November 2010 - 10:24 PM, said:

PBL despite your respect, the Memory Clear button is missing

LynnL, despite your respect you do not need a MemoryClear in a RPN calculator
If your really wnt to clear memory, because you don't understabd how it works, just
0
enter
menSet
pop
:)

Grrrrr !!!
Was This Post Helpful? 0
  • +
  • -

#6 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8316
  • View blog
  • Posts: 31,836
  • Joined: 06-March 08

Posted 23 November 2010 - 10:47 PM

View PostNeoTifa, on 22 November 2010 - 03:57 PM, said:

Peebles, honey, darling! Beautiful I must say, but could you explian the advantages/disadvantages of a rpn calculuator versus a regular standard calculator?

Honey, I'll go explain it to you in private anytime :)
RPN calculators are really really more computer specialists' oriented than any other
They do not have a "clear" button, they do not have () ... you do not require them

All operations are performed with a stack (and my GUI show the stack which the calculators in the 70s didn't). I have added even a button to poke from the stack.

OK the grammar may seems ackward at the beginning but when you are use to it you cannoit imagine you have used another calculator before.

So there is a display which we will call the X register
And stack with an infinite number of elements (thanks to Java) and we will name the first element in the stack the Y register
All operations (almost but few in GREEN in my GUI) are performed using the the X and Y register.
When you perform an operation, X and Y registers are used, the stack is popped and the result is in register X

Let see a simple operation
(3 + 4) * (5 + 10)
in a regular calculator.... I don't know in a RPN calculator
3 <enter> 3 is pushed on the stack
4 X register == 4
+ the 3 in the stack is added to 4 result 7 put in the X register
<enter> the 7 is pushed on the stack
5 so X register contains 5
<enter> the 5 goes into the Y register the preceding 7 is puhed up
10 X register constains 10
+ the 10 and the 5 are added giving 15 stored in the X register... the stack is popped the previous piushed 7 is now on the Y register
+ the 15 in X register and the 7 in Y register are added
X register constains 22
+
Was This Post Helpful? 0
  • +
  • -

#7 NeoTifa  Icon User is offline

  • Whorediot
  • member icon





Reputation: 2498
  • View blog
  • Posts: 15,465
  • Joined: 24-September 08

Posted 24 November 2010 - 02:39 PM

Thank you, Sweetums. ;) So basically it is just like a postfix operator calculator?
Was This Post Helpful? 0
  • +
  • -

#8 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8316
  • View blog
  • Posts: 31,836
  • Joined: 06-March 08

Posted 25 November 2010 - 07:53 PM

A few enhancements and bug fixes:

Bug:
- there was an error when computing available digits for positive long when near Long.MAX_VALUE
- the "NOT" operator popped the stack

Enhancements:
- the second display inot limited to Binary. Now it can display Binary, Decimal, Hexa and Ascii
- help has been added over each button (this feature can be disabled by a JCheckBox)
- different colors identify buttons who do not affect the stack, who operates only with long and/or integer
- the Interface ButtonCallBack used by only one class has been removed

AsciiPanel.java

import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.table.*;

import java.awt.*;

/*
 * To display the ascii characters set
 */
public class AsciiPanel extends JPanel {

	private static final long serialVersionUID = 1L;
	
	// the ascii representation of the first 32 control characters
	private static final String[] control = {"nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", "bsp", "tab",
		"lf", "vt", "np", "cr", "so", "si", "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", "can",
		"em", "eof", "esc", "fs", "gs", "rs", "us"};
	// the header of the JTable
	private static String[] header = {"Dec", "Hex", "Char"};
	
	private JTable table;
	private JViewport viewport;
	// last called (init to last one surely not showed at startup)
	private int lastCode = 255;
	// table model which implements CellRenderer
	private Model model;
	// not to create a TableModelEvent at each time the character changes
	// we will pre-create the 256 of them
	private TableModelEvent[] tme = new TableModelEvent[256];
	// the checkBox that says if we should scroll the AsciiPanel to show the character
	private JCheckBox checkBox;
	
	/*
	 * Constructor
	 */
	AsciiPanel(JCheckBox checkBox) {
		super(new BorderLayout());
		this.checkBox = checkBox;
		// header at the top in the North Region
		JLabel l = new JLabel("Ascii");
		l.setHorizontalAlignment(SwingConstants.CENTER);
		l.setOpaque(true);
		l.setBackground(Color.YELLOW);
		l.setForeground(Color.BLUE);
		add(l, BorderLayout.NORTH);
		
		// creation of the table
		model = new Model();
		table = new MyTable();
		JScrollPane scrollPane = new JScrollPane(table);
		viewport = scrollPane.getViewport();
		add(scrollPane, BorderLayout.CENTER);
		setBorder(BorderFactory.createLineBorder(Color.BLACK));
		
		// creates a model event for every row
		for(int i = 0; i < 255; ++i)
			tme[i] = new TableModelEvent(model, i);
	}
	
	// Assumes table is contained in a JScrollPane. Scrolls the
	// cell (rowIndex, vColIndex) so that it is visible within the viewport.
	private void scrollToVisible(int rowIndex, int vColIndex) {
	    // This rectangle is relative to the table where the
	    // northwest corner of cell (0,0) is always (0,0).
	    Rectangle rect = table.getCellRect(rowIndex, vColIndex, true);

	    // The location of the viewport relative to the table
	    Point pt = viewport.getViewPosition();

	    // Translate the cell location so that it is relative
	    // to the view, assuming the northwest corner of the
	    // view is (0,0)
	    rect.setLocation(rect.x -pt.x, rect.y -pt.y);

	    // Scroll the area into view
	    viewport.scrollRectToVisible(rect);
	}

	/*
	 * The method invoked by the DisplayPanel saying which Ascii code to show
	 */
	void showAscii(int code) {
		// if no change
		if(lastCode == code) {
			return;
		}
		// save last code to fired TableModelEvent if required
		table.tableChanged(tme[lastCode]);
		table.tableChanged(tme[code]);
		lastCode = code;
		// if checkbox is disabled exit
		if(!checkBox.isSelected())
			return;
		// else display the last code
		scrollToCode();
	}
	
	/*
	 * Scroll to JTable to show the code
	 */
	void scrollToCode() {
		// position the scrolling region (about 30 normally showed so make it as we
		// wanted to show row  @to do center the stuff
		scrollToVisible(lastCode, 0);
	}
	/*
	 * Extends JTable just to be able to set the cell renderer
	 */
	class MyTable extends JTable {

		private static final long serialVersionUID = 1L;
		// to save the model which is also the renderer
		
		MyTable() {
			super(model);
		}
		/** tell to call the Model which is also a renderer for all rendering (we return JLabel) */
		public TableCellRenderer getCellRenderer(int row, int col) {
			return model;
		}

	}
	/*
	 * The model for the JTable
	 */
	class Model extends AbstractTableModel implements TableCellRenderer {

		private static final long serialVersionUID = 1L;
		
		private JLabel label;
		// contructor
		Model() {
			// prepare the JLabel tha I will return
		    label = new JLabel();
		    label.setHorizontalAlignment(SwingConstants.CENTER);
		    label.setOpaque(true);
		    label.setForeground(Color.BLUE);		
		}

		// decimal, hexa and the ascii value
		public int getColumnCount() {
			return 3;
		}
        // the 256 asii code
		public int getRowCount() {
			return 256;
		}
        
		public String getColumnName(int col) {
			return header[col];
		}
		@Override
		// just return an object we have a cell renderer anyhow
		public Object getValueAt(int row, int col) {
			return "";
		}

		@Override
		public Component getTableCellRendererComponent(JTable arg0,
				Object arg1, boolean arg2, boolean arg3, int row, int col) {
		    if(row == lastCode)
		    	label.setBackground(Color.YELLOW);
		    else
		    	label.setBackground(Color.WHITE);
		    
		    // column 0 return the decimal value
			if(col == 0) {
				label.setText(String.format("%d", row));
			} else if(col == 1) {    // column 1 the hex value
				label.setText(String.format("x%X", row));
			} else  { // the ascii representation
				if(row < control.length) {
					label.setText("<" + control[row] + ">");
				}
				else {
					String str = Character.toString((char) row);
				    label.setText(str);
				}
			}
			
			return label;
		}
		
	}
}



CalculatorGUI.java

import javax.swing.*;

import java.awt.*;
import java.awt.event.*;
/**
 * The RpnCalculator GUI
 */
public class CalculatorGUI extends JFrame implements KeyListener, ActionListener, MouseListener {

	private static final long serialVersionUID = 1L;

	private static final String copyRight = "Copyright  2010 Les Services Informatiques PBL";
	private static final Color darkGreen = new Color(0, 110, 0);
	// the JLabel where to enter data
	private MainDisplay mainDisplay;
	// we have to save it for the calls back when the word size of the base change
	private StackPanel stackPanel;
	// the buttons for keyboard access
	private RpnButton rpnButton;
	// The AsciiPanel for call back when scrolling is enabled
	private AsciiPanel asciiPanel;
	// The checkbox if we scroll the ascii table and if we display the help
	private JCheckBox asciiCheckBox, helpCheckBox;
	// The label for context help
	private JLabel helpLabel;
	// the CenterRegion
	private VariableGridPanel centerRegion;
	
	/**
	 * Constructor
	 */
	CalculatorGUI() {
		super("RPN Calculator");

		// First the panels that are independant from the others
		SecondDisplay secondDisplay = new SecondDisplay(this);
		
		// the panel that shows the Memory Register
		MemoryPanel memoryPanel = new MemoryPanel(this);
			
		// the JCheckBox for ascii scrolling
		asciiCheckBox = new JCheckBox("Scroll Ascii Table");
		asciiCheckBox.addActionListener(this);
		asciiCheckBox.addMouseListener(this);
		// the checkBox for help display
		helpCheckBox = new JCheckBox("Display buttons help", true);
		helpCheckBox.addActionListener(this);
		helpCheckBox.addMouseListener(this);
		// the panel offering the RadioButtons and the checkedBoxes
		JPanel wordBasePanel = new WordSizeAndBasePanel(this, asciiCheckBox, helpCheckBox);

		// create the stack
		RpnStack stack = new RpnStack(this);
		// the stackPanel was built when the RpnStatck was built
		stackPanel = stack.getPanel();
	
		// the ascii panel
		asciiPanel  = new AsciiPanel(asciiCheckBox);
		
		// the main display that display the output led abd performs the opeartion
		mainDisplay = new MainDisplay(stack, secondDisplay, memoryPanel, asciiPanel);

		// generate the buttons
		rpnButton = new RpnButton(mainDisplay, this);
		// the Panel that display the Calculator buttons
		// needs to be called once the rpnButtons has been created
		CenterPanel centerPanel = new CenterPanel();		

		// the led display of the calculator
		JLabel label = mainDisplay.getLabel();
		label.setOpaque(true);
		label.setBackground(Color.WHITE);
		label.setHorizontalAlignment(SwingConstants.RIGHT);
		label.addMouseListener(this);
		// put in a ScrollPane if ever it becomes really big
		JScrollPane displayPane = new JScrollPane(label);
		displayPane.setBorder(BorderFactory.createLineBorder(Color.BLUE));
	
		// the JLabel to display Help
		helpLabel = new JLabel(copyRight);
		helpLabel.setOpaque(true);
		helpLabel.setBackground(Color.WHITE);
		helpLabel.addMouseListener(this);
		
		// position all our internal panels
		//---------------------------------
		// The panel we will put in the center containing all the others
		centerRegion = new VariableGridPanel(20, 5, 3, 3);

		// the main display (the led) at the top left
		centerRegion.addComp(displayPane, 0, 0, 3, 2);
		// put the memoryPanel in
		centerRegion.addComp(memoryPanel, 2, 0, 1, 3);
		// the panel that offer the RadioButtons for base and word size and the checkboxes on the left just under 

memory
		centerRegion.addComp(wordBasePanel, 5, 0, 1, 14);
		// the secondary display panel under the first display
		centerRegion.addComp(secondDisplay, 2, 1, 2, 4);
		// the Panel that display the Calculator buttons just under it
		centerRegion.addComp(centerPanel, 6, 1, 2, 13);
		// the stack panel at the left
		centerRegion.addComp(stackPanel, 0, 3, 1, 19);
        // and the Ascii panel at its right
		centerRegion.addComp(asciiPanel, 0, 4, 1, 19);
		// the help
		centerRegion.addComp(helpLabel, 19, 0, 5, 1);
		
		// all that in the Center Region
		add(centerRegion, BorderLayout.CENTER);

		// for keyboard access
		this.setFocusable(true);
		addKeyListener(this);
	}

	// to display the help message
	protected void setHelpText(String str) {
		// if help display is enabled
		if(helpCheckBox.isSelected()) {		
			if(str == null) {
				helpLabel.setForeground(Color.BLACK);
				helpLabel.setText(copyRight);
			}
			else {
				helpLabel.setForeground(darkGreen);
				helpLabel.setText(str);
			}
		}
	}
	// change in the base inform everybody who should be notified about it
	protected void setBase(int baseIdx) {
		// enable/disable the buttons depending of the base
		RpnButton.setBase(baseIdx);
		// change the display in the stack
		stackPanel.setBase(baseIdx);
		mainDisplay.setBase(baseIdx);
		this.requestFocus();
	}
	// change in the word size inform everybody who should be notified about it
	protected void setWordSize(int wordSize) {
		mainDisplay.setWordSize(wordSize);
		stackPanel.setWordSize(wordSize);
		this.requestFocus();
	}

	
	// called by the StackPanel to set the register X to a certain value
	void setReg0(StackElement se) {
		// will be null if coming from MemoryPanel
		if(se == null) 
			mainDisplay.setReg0(null);
		else   // clone it before sending it in
			mainDisplay.setReg0(new StackElement(se));
	}
	/*
	 * A listener on the checkbox just to reset 
	 * the focus on the JFrame
	 */
	public void actionPerformed(ActionEvent e) {
		Object o = e.getSource();
		// keep the focus on the frame for the KeyListener
		if(o == asciiCheckBox) {
			asciiPanel.scrollToCode();
		}
		else {						// the help enabled/disable
			// if disabled put the copyright
			if(!helpCheckBox.isSelected()) {
				helpLabel.setForeground(Color.BLACK);
				helpLabel.setText(copyRight);
			}
		}
		this.requestFocus();
	}

	/* A key is entered on the keyboard. Thansfer to our RpnButton */
	public void keyPressed(KeyEvent e) {
		char c = e.getKeyChar();
		int code = e.getKeyCode();
		rpnButton.keyTyped(c, code);
	}

	/* For the Help of the CheckBox */
	public void mouseEntered(MouseEvent e) {
		Object o = e.getSource();
		if(o == asciiCheckBox)
			setHelpText("Check this button if you want the Ascii table to scroll automatically to the lowest 

byte value in the X register.");
		else if(o == helpCheckBox)
			setHelpText("Check this button to have a help text displayed at the bottom of the screen when your 

mouse is over a button.");
		else if(o == helpLabel)
			setHelpText("To disable multiple messages displayed in green here check off the \"Display buttons 

help\" CheckedBox at the left of the panel.");
		else   // so it has to be the master display
			setHelpText("The master register second operand of all operations. Named X in Help messages 

displayed");
	}

	// clear the help message
	public void mouseExited(MouseEvent e) {
		setHelpText(null);
	}
	public void keyTyped(KeyEvent e) {}
	public void keyReleased(KeyEvent arg0) {}
	public void mouseClicked(MouseEvent arg0) {}
	public void mousePressed(MouseEvent arg0) {}
	public void mouseReleased(MouseEvent arg0) {}

	/**
	 * To start the whole applicatio
	 */
	public static void main(String[] args) {

		// prepare the GUI to run in another thread
		Runnable detach = new Runnable() {
			public void run() {
				CalculatorGUI rpncGui= new CalculatorGUI();
				// make it visible and display it
				rpncGui.setBounds(100, 100, 750, 500);
				rpncGui.setVisible(true);
				rpncGui.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
			}
		};
		// start the thread
		SwingUtilities.invokeLater(detach);
	}
}



CenterPanel.java

/**
 * 
 */
public class CenterPanel extends VariableGridPanel {

	private static final long serialVersionUID = 1L;
	
	/**
	 * Constructor 
	 */
    CenterPanel() {
    	super(6, 7, 2, 2);
    	
       	addComp(RpnButton.getButton(RpnButton.BTN_FLIP), 0, 0);
    	addComp(RpnButton.getButton(RpnButton.BTN_CLEAR), 0, 1);
       	addComp(RpnButton.getButton(RpnButton.BTN_A), 0, 2);
       	addComp(RpnButton.getButton(RpnButton.BTN_POW), 0, 3);
           	
    	addComp(RpnButton.getButton(RpnButton.BTN_AND_NOT), 1, 0);
    	addComp(RpnButton.getButton(RpnButton.BTN_ABS), 1, 1);
    	addComp(RpnButton.getButton(RpnButton.BTN_B)/>, 1, 2);
    	addComp(RpnButton.getButton(RpnButton.BTN_BSP), 1, 3);
    	addComp(RpnButton.getButton(RpnButton.BTN_POP), 1, 4);
    	addComp(RpnButton.getButton(RpnButton.BTN_C), 2, 2);
    	addComp(RpnButton.getButton(RpnButton.BTN_D), 3, 2);
    	addComp(RpnButton.getButton(RpnButton.BTN_E), 4, 2);
    	addComp(RpnButton.getButton(RpnButton.BTN_F), 5, 2);
    	
       	addComp(RpnButton.getButton(RpnButton.BTN_SEVEN), 2, 3);
       	addComp(RpnButton.getButton(RpnButton.BTN_HEIGHT), 2, 4);
       	addComp(RpnButton.getButton(RpnButton.BTN_NINE), 2, 5);
       	addComp(RpnButton.getButton(RpnButton.BTN_MULT), 2, 6);
       	
       	addComp(RpnButton.getButton(RpnButton.BTN_FOUR), 3, 3);
       	addComp(RpnButton.getButton(RpnButton.BTN_FIVE), 3, 4);
       	addComp(RpnButton.getButton(RpnButton.BTN_SIX), 3, 5);
       	addComp(RpnButton.getButton(RpnButton.BTN_MINUS), 3, 6);
       	
       	addComp(RpnButton.getButton(RpnButton.BTN_ONE), 4, 3);
       	addComp(RpnButton.getButton(RpnButton.BTN_TWO), 4, 4);
       	addComp(RpnButton.getButton(RpnButton.BTN_THREE), 4, 5);
       	addComp(RpnButton.getButton(RpnButton.BTN_PLUS), 4, 6);
       	
       	addComp(RpnButton.getButton(RpnButton.BTN_ZERO), 5, 3, 2, 1);
       	addComp(RpnButton.getButton(RpnButton.BTN_ENTER), 5, 5, 2, 1);
       	
       	addComp(RpnButton.getButton(RpnButton.BTN_ROT_L), 2, 0);
       	addComp(RpnButton.getButton(RpnButton.BTN_ROT_R), 2, 1);
       	addComp(RpnButton.getButton(RpnButton.BTN_SHIFT_L), 3, 0);
       	addComp(RpnButton.getButton(RpnButton.BTN_SHIFT_R), 3, 1);
       	addComp(RpnButton.getButton(RpnButton.BTN_OR), 4, 0);
       	addComp(RpnButton.getButton(RpnButton.BTN_XOR), 4, 1);
       	addComp(RpnButton.getButton(RpnButton.BTN_AND), 5, 0);
       	addComp(RpnButton.getButton(RpnButton.BTN_NOT), 5, 1);
  
       	addComp(RpnButton.getButton(RpnButton.BTN_MEM_SET), 0, 4);
       	addComp(RpnButton.getButton(RpnButton.BTN_MEM_RECALL), 0, 5);
       	addComp(RpnButton.getButton(RpnButton.BTN_RMD), 0, 6);
       	
       	addComp(RpnButton.getButton(RpnButton.BTN_CHANGE_SIGN), 1, 5);
       	addComp(RpnButton.getButton(RpnButton.BTN_DIV), 1, 6);
              	
     }    
}



MainDisplay.java

import javax.swing.*;

import java.awt.*;
import java.math.BigInteger;

/**
 * The main display where user enters value
 *
 */
public class MainDisplay {

	// the basic digit stuff
	private static String displayCode = "0123456789ABCDEF"; 

	// the baseIdx to use default to Decimal at construction time
	private int baseIdx = StackElement.DECIMAL_IDX;
	// the translation in base
	private static final int[] base = {16, 10, 8, 2};
	// the max size in hex and bin and octal
	private static final int[] hexMaxSize = {0, 16, 8, 4, 2};
	private static final int[] binMaxSize = {0, 64, 32, 16, 8};
	// for the max and min in decimal
	private static final BigInteger bigMax = new BigInteger("" + Long.MAX_VALUE);
	private static final BigInteger bigMin = new BigInteger("" + Long.MIN_VALUE);
	
	// the panel that display MemoryContent
	private MemoryPanel memoryPanel;
	
	// the word size
	private int wordSize = StackElement.SIZE_INT;

	// the StackElement that I display
	private StackElement display = new StackElement("0", baseIdx, StackElement.SIZE_INT);
	// end the memory
	private StackElement memory = new StackElement("0", baseIdx, StackElement.SIZE_INT);

	// the JLabel to display user's number in the base
	private JLabel label;
	// the actual number to be display the JLabel 
	private String numberStr;
	// if the lastOparation was "enter". In that case next digit entered clear the display
	private boolean lastOperationWasEnter;

	// the stack
	private RpnStack stack;
	// the second display
	private SecondDisplay secondDisplayPanel;
	// the AsciiPanel
	private AsciiPanel asciiPanel;

	/**
	 * Constructor
	 */
	protected MainDisplay(RpnStack stack, SecondDisplay secondDisplayPanel, MemoryPanel memoryPanel, AsciiPanel 

asciiPanel) {

		this.stack = stack;
		this.secondDisplayPanel = secondDisplayPanel;
		this.memoryPanel = memoryPanel;
		this.asciiPanel = asciiPanel;
		// the label that display the number
		numberStr = "0";

		// the label at the top that display based on the base
		label = new JLabel(numberStr + " ");

		// increase the size of the digit
		Font font = label.getFont();
		label.setFont(font.deriveFont(font.getSize2D() * 1.4f));
		// init the first ascii showed
		asciiPanel.showAscii(0);
	}

	// call back when a button is clicked
	public void buttonclick(int code) {

		// if it is a digit button we call the method that appends
		if(code <= RpnButton.BTN_F) {
			userTyped(code);
			return;
		}
		// big switch for the other
		switch(code) {

		// enter on: we simply have to push on the stack a copy of the display
		case RpnButton.BTN_ENTER:
			StackElement se = new StackElement(display);
			stack.push(se);
			lastOperationWasEnter = true;
			updateDisplay();
			break;
			
		// a pop
		case RpnButton.BTN_POP:
			display = stack.pop();
			numberStr = display.toString(wordSize, baseIdx);
			updateDisplay();
			break;
			

		// backspace as been entered
		case RpnButton.BTN_BSP:
			processBSP();
			break;

		// a change sign
		case RpnButton.BTN_CHANGE_SIGN:
			processChangeSign();
			break;

		// + - * /
		case RpnButton.BTN_DIV:
		case RpnButton.BTN_PLUS:
		case RpnButton.BTN_MINUS:
		case RpnButton.BTN_MULT:
		case RpnButton.BTN_POW:
		case RpnButton.BTN_RMD:
		// logical
		case RpnButton.BTN_OR:
		case RpnButton.BTN_XOR:
		case RpnButton.BTN_AND:			
		case RpnButton.BTN_AND_NOT:
		case RpnButton.BTN_FLIP:
		case RpnButton.BTN_CLEAR:
		// shift
		case RpnButton.BTN_SHIFT_L:
		case RpnButton.BTN_SHIFT_R:
			processOperationCode(code);
			break;
		
		// no statck access
		case RpnButton.BTN_NOT:
		case RpnButton.BTN_ABS:
			performNoPopOperation(code);
			break;
			
		case RpnButton.BTN_ROT_L:
		case RpnButton.BTN_ROT_R:
			performRotateOperation(code);
			break;
		
		// memory operation
		case RpnButton.BTN_MEM_SET:
		case RpnButton.BTN_MEM_RECALL:
			performMemOperation(code);
			break;
		}
		
	}

	// user has type a digit to append to the display
	private void userTyped(int code) {
		// length of the display number in BigInteger
		int ilen = numberStr.length();
		// get ride of the leading "0" if there is one
		if((ilen == 1 && numberStr.charAt(0) == '0') || lastOperationWasEnter) {
			numberStr = "";
			ilen = 0;
			// reset the fact that the last operation was an "enter"
			lastOperationWasEnter = false;
		}
		// reset the String value in BigInteger
		numberStr += displayCode.charAt(code);
		updateDisplay();
	}

	/*
	 * Update label display based on base and word size
	 */
	private void updateDisplay() {
		// reset our value based on that String
		display.setValue(numberStr, baseIdx, wordSize);	
		// get the value for that type based on base
		label.setText(display.toString(wordSize, baseIdx) + " ");
		asciiPanel.showAscii(display.getInt() & 0xFF);
		// display the second displays (using long as max)
		int tmpSize = wordSize;
		if(tmpSize == StackElement.SIZE_BIG)
			tmpSize = StackElement.SIZE_LONG;
		// get longValue for decimal panel
		long longValue = display.getLong();
		secondDisplayPanel.refresh(wordSize, 
				display.toString(tmpSize, StackElement.BINARY_IDX),
				display.toString(tmpSize, StackElement.HEXADECIMAL_IDX),
				longValue);
		// reset by default the digit buttons enabled or not
		RpnButton.setBase(baseIdx);
		// enable/disable the / and Reminder buttons if divisor == 0
		boolean divEnabled = true;
		if(numberStr.length() == 1 && numberStr.charAt(0) == '0') 
			divEnabled = false;
		RpnButton.getButton(RpnButton.BTN_DIV).setEnabled(divEnabled);
		RpnButton.getButton(RpnButton.BTN_RMD).setEnabled(divEnabled);
		RpnButton.getButton(RpnButton.BTN_BSP).setEnabled(divEnabled);
		
		// if last operation was a push does not disable some buttons
		if(lastOperationWasEnter)
			return;
		
		// there are no buttons to enable disable for BigInteger
		// all math operations are permitted and we can append digit to it
		if(wordSize == StackElement.SIZE_BIG)
			return;
		
		int ilen = numberStr.length();
		// in hexa and binary easy the number of digits entered let us konw if we can continue
		if(baseIdx == StackElement.HEXADECIMAL_IDX) {
			if(ilen < hexMaxSize[wordSize])
				return;
			disableBut(0);
			return;
		}
		if(baseIdx == StackElement.BINARY_IDX) {
			if(ilen < binMaxSize[wordSize])
				return;
			disableBut(0);
			return;
		}
		// in octal, we have to see if the binary representation of this octal number has room for 3 bits
		if(baseIdx == StackElement.OCTAL_IDX) {
			// get binary representation
			String str = display.toString(wordSize, StackElement.BINARY_IDX);
			if(str.length() > binMaxSize[wordSize] - 3)
				disableBut(0);
			return;
		}
		
		// we will use the long value to perform our tests
		// as Java promotes byte and short operation so int anyway
		long val = display.getLong();
		// determine which button should be disabled as they would overload the word size
		if(val == 0)   // 0 nothing to do 
			return;
		
		// Get the min and max value for that word size
		long max = StackElement.getMaxValue(wordSize);
		long min = StackElement.getMinValue(wordSize);
		// for byte, short, int and long you can always change a positive number
		// to its negative value.  This not the case for MIN_BYTE, MIN_SHORT,....
		// for example byte go from -128 to 127 and short from -32768 to 32767. 
		// If the value is the minimum value for the type we have to disable the +/- button
		if(val == min) 
			RpnButton.getButton(RpnButton.BTN_CHANGE_SIGN).setEnabled(false);
		// if one of any end disable all
		if(val == min || val == max) {
			disableBut(0);
			return;			
		}
		// for byte, short, int I  can test in a long
		// for long I will have to use a BigInteger
		// test first in within 10 times the size
		if(val > min / 10 && val < max / 10) {
			return;
		}
		
		// number that we can add to the (actual value * 10)
		long free;
		// we will have to use BigInteger
		if(wordSize == StackElement.SIZE_LONG) {
			// create BigInteger 10 times as big as our number
			BigInteger bigVal = new BigInteger(numberStr + "0");
			BigInteger bigFree;
			// positive
			if(val > 0) {
				if(bigVal.compareTo(bigMax) > 0) {
					disableBut(0);
					return;
				}
				bigFree = bigMax.subtract(bigVal);
			}
			else {						// negative
				if(bigVal.compareTo(bigMin) < 0) {
					disableBut(0);
					return;
				}
				bigFree = bigMax.subtract(bigVal);
				bigFree = bigFree.negate();
			}
			free = bigFree.longValue();
		}
		else {
			// for smaller than long I can perform the calculation in long
			// see if we can multiply by 10 to add another digit
			long times10 = val * 10;
			if(times10 < min || times10 > max) {
				disableBut(0);							// disable all buttons
				return;
			}
			// see if all digits are available
			// positive value
			if(val > 0) {
				free = max - times10;      // how many can I add after I multiply by 10
			}
			else {
				free = min - times10;
				free = -free;				// make it positive for single testing
			}
		}
		// so disable everything higer
		if(free < 9)
			disableBut((int) free + 1);
	}
	
	/*
	 * Disable buttons from N to F
	 */
	private void disableBut(int from) {
		for(int i = from; i < base[baseIdx]; i++)
			RpnButton.setOutOfRange(i);		
	}
	/*
	 * Change in the base
	 */
	void setBase(int baseIdx) {
		this.baseIdx = baseIdx;
		numberStr = display.toString(wordSize, baseIdx);
		updateDisplay();
		String str = memory.toString(wordSize, baseIdx);
		memoryPanel.setText(str);
	}
	/*
	 * Change in the word size
	 */
	void setWordSize(int wordSize) {
		this.wordSize = wordSize;
		numberStr = display.toString(wordSize, baseIdx);
		updateDisplay();
		String str = memory.toString(wordSize, baseIdx);
		memoryPanel.setText(str);
	}

	/*
	 * Call from the JFrame which was called by the StackPanel or the MemoryPanel so set the Reg0
	 * to a certain stack value
	 */
	protected void setReg0(StackElement se) {
		// if se == null it is for the memory register
		if(se == null) {
			performMemOperation(RpnButton.BTN_MEM_RECALL);
			return;
		}
		// OK coming from StackPanel
		display = se;
		numberStr = display.toString(wordSize, baseIdx);
		updateDisplay();
	}
	/*
	 * For the CenterPanel to access my label and pane
	 */
	JLabel getLabel() {
		return label;
	}

	// handles a BackSpace
	private void processBSP() {
		// user decided to keep that number
		lastOperationWasEnter = false;
		// if len == 1 then just put a 0
		int ilen = numberStr.length();
		if(ilen == 1) {
			numberStr = "0";
			updateDisplay();
			return;
		}
		// if len == 2 but first one is '-'
		if(ilen == 2 && numberStr.charAt(0) == '-') {
			numberStr = "0";
			updateDisplay();
			return;					
		}
		// remove last digit
		numberStr = numberStr.substring(0, ilen-1);
		updateDisplay();
	}

	// process the +/-
	private void processChangeSign() {
		// we are in decimal because +/- is disabled in other modes
		if(numberStr.length() == 1 && numberStr.charAt(0) == '0') 
			return;

		// reset the fact that the last operation was an "enter"
		lastOperationWasEnter = false;
		// check if first digit is a -
		if(numberStr.charAt(0) == '-')
			numberStr = numberStr.substring(1);
		else
			numberStr = "-" + numberStr;
		// redo calculations
		updateDisplay();		
	}
	
	// operations without pop
	private void performNoPopOperation(int code) {
		BigInteger val = display.getBigInteger();
	    BigInteger result = null;
	    switch(code) {
			case RpnButton.BTN_NOT:
				result = val.not();
				break;
			case RpnButton.BTN_ABS:
				result = val.abs();
				break;
	    }
		numberStr = result.toString(base[baseIdx]);
		// update our value	
		updateDisplay();
		
	}
	// memory clear/set/recall
	private void performMemOperation(int code) {
		
		switch(code) {
			case RpnButton.BTN_MEM_SET:
				memory = new StackElement(display);
				break;
			case RpnButton.BTN_MEM_RECALL:
				display = new StackElement(memory);
				lastOperationWasEnter = false;
				numberStr = display.toString(wordSize, baseIdx);
				updateDisplay();
				return;            // <--- return no need to update memory display
	    }
		// for clear and set update mem display
		String str = memory.toString(wordSize, baseIdx);
		memoryPanel.setText(str);
	}
	
	// process operations in big integer
	private void processOperationCode(int code) {
		
		BigInteger opr = stack.pop().getBigInteger();
		BigInteger val = display.getBigInteger();
	    BigInteger result = null;
		// as for a push/enter next character entered will clear display
		// no need to disable out of range digit
		lastOperationWasEnter = true;
		switch(code) {
			case RpnButton.BTN_PLUS:
				result = opr.add(val);
				break;
			case RpnButton.BTN_MINUS:
				result = opr.subtract(val);
				break;
			case RpnButton.BTN_MULT:
				result = opr.multiply(val);
				break;
			case RpnButton.BTN_DIV:
				result = opr.divide(val);
				break;
			case RpnButton.BTN_OR:
				result = opr.or(val);
				break;
			case RpnButton.BTN_XOR:
				result = opr.xor(val);
				break;
			case RpnButton.BTN_AND:
				result = opr.and(val);
				break;
			case RpnButton.BTN_AND_NOT:
				result = val.not();
				result = result.and(opr);
				break;
			case RpnButton.BTN_POW:
				result = opr.pow(display.getInt());
				break;
			case RpnButton.BTN_SHIFT_L:
				result = opr.shiftLeft(display.getInt());
				break;
			case RpnButton.BTN_SHIFT_R:
				result = opr.shiftRight(display.getInt());
				break;
			case RpnButton.BTN_RMD:
				result = opr.remainder(val);
				break;
			case RpnButton.BTN_CLEAR:
			    result = opr.clearBit(display.getInt());
			    break;
			case RpnButton.BTN_FLIP:
			    result = opr.flipBit(display.getInt());
			    break;
			    
		}
		numberStr = result.toString(base[baseIdx]);
		// update our value	
		updateDisplay();
	}
	
	// rotate can only performed on long
	private void performRotateOperation(int code) {
		// the number that will be rotate
		StackElement num = stack.pop();
		// local variable for 
		int i, shift;
		String str;
		// as for a push/enter next character entered will clear display
		// no need to disable out of range digit
		lastOperationWasEnter = true;
		
		switch(wordSize) {
			case StackElement.SIZE_LONG:
				long l;
				if(code == RpnButton.BTN_ROT_L)
					l = Long.rotateLeft(num.getLong(), display.getInt());
				else
					l = Long.rotateRight(num.getLong(), display.getInt());
				str = String.format("%d", l);
				display.setValue(str, StackElement.DECIMAL_IDX, StackElement.SIZE_LONG);
				break;
					
			case StackElement.SIZE_INT:
				if(code == RpnButton.BTN_ROT_L)
					i = Integer.rotateLeft(num.getInt(), display.getInt());
				else
					i = Integer.rotateRight(num.getInt(), display.getInt());
				str = String.format("%d", i);
				display.setValue(str, StackElement.DECIMAL_IDX, StackElement.SIZE_INT);
				break;
	
			case StackElement.SIZE_SHORT:
				i = num.getShort();
				// remove signe extension
				i &= 0xFFFF;
				shift = i << 16;
				// copy into higher bit
				i |= shift;
				if(code == RpnButton.BTN_ROT_L)
					i = Integer.rotateLeft(i, display.getInt());
				else
					i = Integer.rotateRight(i, display.getInt());
				i &= 0xFFFF;
				str = String.format("%d", i);
				display.setValue(str, StackElement.DECIMAL_IDX, StackElement.SIZE_SHORT);
				break;
				
			case StackElement.SIZE_BYTE:
				i = num.getByte();
				// remove sign extension
				i &= 0xFF;            // 000x
				shift = i << 8;       // 00x0
				shift |= i;           // 00xx
				shift <<= 8;          // 0xx0
				shift |= i;           // 0xxx
				shift <<= 8;          // xxx0
				shift |= i;           // xxxx
				i = shift;
				if(code == RpnButton.BTN_ROT_L)
					i = Integer.rotateLeft(i, display.getInt());
				else
					i = Integer.rotateRight(i, display.getInt());
				i &= 0xFF;
				str = String.format("%d", i);
				display.setValue(str, StackElement.DECIMAL_IDX, StackElement.SIZE_BYTE);
				break;
		}
		
		// have it back to actual representation
		numberStr = display.toString(wordSize, baseIdx);
		updateDisplay();
	}
}



MemoryPanel.java

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/*
 * Display the memory word
 */
public class MemoryPanel extends JPanel implements MouseListener {
	
	private static final long serialVersionUID = 1L;

	private JLabel displayLabel;
	private CalculatorGUI father;
	
	MemoryPanel(CalculatorGUI father) {
		super(new BorderLayout());
		this.father = father;
		setBorder(BorderFactory.createLineBorder(Color.BLACK));
		
		// the header
		JLabel label = new JLabel("Memory");
		label.setHorizontalAlignment(SwingConstants.CENTER);
		add(label, BorderLayout.NORTH);
	
		// to display word stored in memory
		displayLabel = new JLabel("0 ");
		displayLabel.setOpaque(true);
		displayLabel.setBackground(Color.WHITE);
		displayLabel.setForeground(Color.BLUE);
		displayLabel.setHorizontalAlignment(SwingConstants.RIGHT);
		displayLabel.addMouseListener(this);
		add(new JScrollPane(displayLabel), BorderLayout.CENTER);
		
		// add a border by putting 2 empty label east and west
		add(new JLabel("  "), BorderLayout.EAST);
		add(new JLabel("  "), BorderLayout.WEST);
		add(new JLabel("  "), BorderLayout.SOUTH);
	}
	
	/*
	 * To update memory display
	 */
	void setText(String str) {
		displayLabel.setText(str + " ");
	}

    // call main frame to update main display with memory register
	public void mouseClicked(MouseEvent arg0) {
		father.setReg0(null);
	}
	public void mouseEntered(MouseEvent arg0) {
		father.setHelpText("Click on this Memory register to copy it into the X register.");
	}
	public void mouseExited(MouseEvent arg0) {
		father.setHelpText(null);
	}
	public void mousePressed(MouseEvent arg0) {}
	public void mouseReleased(MouseEvent arg0) {}
}



RpnButton.java

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

/*
 * A class that extends JButton to expands there capabilities
 * - degrading of color when mouse leaves it
 * - disabled when related to a base they are irrelevant
 * - all the buttons are made static an registered in a static array so they can be accessed by multiple classes
 */
public class RpnButton  {


	// list of all the codes put the number first we will use their code value
	public static final int BTN_ZERO = 0, 
							BTN_ONE = 1, 
							BTN_TWO = 2, 
							BTN_THREE = 3, 
							BTN_FOUR = 4,
							BTN_FIVE = 5, 
							BTN_SIX = 6, 
							BTN_SEVEN = 7, 
							BTN_HEIGHT = 8, 
							BTN_NINE = 9,
							BTN_A = 10, 
							BTN_B = 11, 
							BTN_C = 12, 
							BTN_D = 13,  
							BTN_E = 14, 
							BTN_F = 15,
							BTN_ROT_L = 16, 
							BTN_ROT_R = 17, 
							BTN_SHIFT_L = 18, 
							BTN_SHIFT_R = 19,
							BTN_OR = 20, 
							BTN_XOR = 21, 
							BTN_NOT = 22, 
							BTN_AND = 23,
							BTN_CLEAR = 24, 
							BTN_MEM_SET = 25, 
							BTN_MEM_RECALL = 26, 
							BTN_CHANGE_SIGN = 27,
							BTN_DIV = 28, 
							BTN_MULT = 29, 
							BTN_PLUS = 30, 
							BTN_MINUS = 31, 
							BTN_ENTER = 32,
							BTN_BSP = 33, 
							BTN_POP = 34, 
							BTN_POW = 35, 
							BTN_RMD = 36, 
							BTN_ABS = 37,
							BTN_FLIP = 38, 
							BTN_AND_NOT = 39; 

	// and their associated Strings. One for the button one for the Help
	private static String[][] label = {
		{"0", "Binary/Octal/Decimal/Hexadecimal 0."}, 
		{"1", "Binary/Octal/Decimal/Hexadecimal 1."}, 
		{"2", "Octal/Decimal/Hexadecimal 2 not available in Binary mode."}, 
		{"3", "Octal/Decimal/Hexadecimal 3 not available in Binary mode."}, 
		{"4", "Octal/Decimal/Hexadecimal 4 not available in Binary mode."}, 
		{"5", "Octal/Decimal/Hexadecimal 5 not available in Binary mode."}, 
		{"6", "Octal/Decimal/Hexadecimal 6 not available in Binary mode."}, 
		{"7", "Octal/Decimal/Hexadecimal 7 not available in Binary mode."}, 
		{"8", "Decimal/Hexadecimal 8 not available in Binary or Octal mode."}, 
		{"9", "Decimal/Hexadecimal 9 not available in Binary or Octal mode."},
		{"A", "Hexadecimal 0xA not enabled in other mode than Hexadecimal mode."}, 
		{"B", "Hexadecimal 0xB not enabled in other mode than Hexadecimal mode."}, 
		{"C", "Hexadecimal 0xC not enabled in other mode than Hexadecimal mode."}, 
		{"D", "Hexadecimal 0xD not enabled in other mode than Hexadecimal mode."}, 
		{"E", "Hexadecimal 0xE not enabled in other mode than Hexadecimal mode."}, 
		{"F", "Hexadecimal 0xF not enabled in other mode than Hexadecimal mode."},
		{"RoL", "Rotate Left the Y register by the number of bits specified in the X register. Stack is popped. 

(Register X Long word size used)."}, 
		{"RoR", "Rotate Right the Y register by the number of bits specified in the X register. Stack is popped. 

(Register X Long word size used)."}, 
		{"LsH", "Shift Left the Y register by the number of bits specified in the X register. (Register X Integer 

word size used)."}, 
		{"RsH", "Shift Right the Y register by the number of bits specified in the X register. (Register X Integer 

word size used)."}, 
		{"Or", "ORs the X and Y registers. Stack is popped."}, 
		{"Xor", "XORs the X and Y registers. Stack is popped."}, 
		{"Not", "Performs a logival NOT of the X register. The stack is NOT popped."}, 
		{"And", "ANDs the X and Y registers. Stack is popped."},		
		{"Clr", "Clears in Y register the bit specified in X register. Stack is popped. (Register X Integer word 

size used)."},
		{"MS", "Copy the X register into Memory register. (No stack operation)."}, 
		{"MR", "Copy the Memory register into X register. (No stack operation)."}, 
		{"", "Changes the sign of the X register. Stack is NOT popped. (Only available in Decimal mode)."}, 
		{"/", "Divides Y register by the X register. Stack is popped. (Disabled when X register equals 0)."}, 
		{"*", "Multiplies Y register by the X register. Stack is popped."}, 
		{"+", "Adds Y register to the X register. Stack is popped."}, 
		{"-", "Subtracts the X register from the Y register. Stack is popped."}, 
		{"Enter", "Pushes the X register into the stack. Next digit entered would clear the X register. Backspace 

and ChangeSign would keep it."}, 
		{"BSP", "Removes the rightmost digit from the X register. (Not available when X register equals 0)."}, 
		{"Pop", "Stack is popped which copies the Y register into the X register."}, 
		{"POW", "Raise the Y register to the power of X register. Stack is popped. (Register X Integer word size 

used)."}, 
		{"RMD", "Performs the modulo of register Y by register X. Stack is popped. (Disabled when X register equals 

0)."}, 
		{"ABS", "Set the X register to absolute value. Stack is NOT popped. (Only available in Decimal mode)."},
		{"Flip", "Flips in Y register the bit specified in X register. Stack is popped. (Register X Integer word 

size used)."}, 
		{"<html><p>And</p><p>Not</p></html", "ANDs the Y register with ~Register X"},
		};

	// the buttons kept in static variable 
	private static Btn[] button = new Btn[label.length];
	// an arraylist to contains all my buttons so when the base changes I can check if they 
	// should be enabled or not
	private static ArrayList<Btn> al = new ArrayList<Btn>(16);

	// a larger Font for the smaller one
	private Font smallFont, largerFont;	
	// the colors for my fadeout
	private Color[] color;
	// the color for the unary operator (do not change the stack)
	private static final Color unaryOprColor = new Color(0, 100, 0);       // dark greeen
	// the operators using that color
	private static final int unaryOpr[] = {BTN_CHANGE_SIGN, BTN_NOT, BTN_BSP, BTN_ABS, BTN_MEM_SET, BTN_MEM_RECALL};
	// the color for the operations that use the Long version of the X register
	private static final Color longColor = new Color(255, 0, 255);
	// the buttons that use X register long value
	private static final int longX[] = {BTN_ROT_L, BTN_ROT_R};
	// the color for the operations that use the Integer version of the X register
	private static final Color intColor = new Color(125, 0, 255);
	// the buttons that use X register long value
	private static final int intX[] = {BTN_SHIFT_L, BTN_SHIFT_R, BTN_CLEAR, BTN_FLIP, BTN_POW};
	// the margin
	private Insets inset = new Insets(1, 1, 1, 1);
	// who I will call the buttonclick method when I am clicked
	private MainDisplay callBack;
    // the JFrame for focus
	private CalculatorGUI master;

	RpnButton(MainDisplay callBack, CalculatorGUI master) {
		this.callBack = callBack;
		this.master = master;

		// build of yellow fading out
		// and the base and the button
		color = new Color[6];
		// start by full white
		int b = 255;
		// loop inversed removing 5 times 30 to b so 255 to 155
		color[5] = new Color(255, 255, B)/>;
		for(int i = 4; i >= 0; --i) {
			b -= 30;
			color[i] = new Color(255, 255, B)/>;
		}	

		// build the larger font and the font for "enter:
		JButton but = new JButton("");
		Font regularFont = but.getFont();
		float size = regularFont.getSize2D();
		smallFont = regularFont.deriveFont(size * 0.8f);
		largerFont = regularFont.deriveFont(size * 1.4f);
		// now generate the button
		for(int i = 0; i < button.length; i++)
			button[i] = new Btn(i);
	}

	/**
	 * To get a button
	 */
	static JButton getButton(int code) {
		return button[code];
	}

	// the base was changed
	protected static void setBase(int idx) {
		// enable/disable button accordingly
		for(Btn b : al) {
			// change sign is an exception
			if(b.code == BTN_CHANGE_SIGN || b.code == BTN_ABS)
				b.setEnabled(idx == StackElement.DECIMAL_IDX);
			else  // the other based on the base
				b.setEnabled(b.baseIdx >= idx);
		}
	}
	
	// disable button outOfRange
	protected static void setOutOfRange(int n) {
		button[n].setOutOfRange();
	}

	// a key was typed on the keyboard
	protected void keyTyped(char c, int keyCode) {
		int idx;
		if(c >= '0' && c <= '9')
			idx = c - '0';
		else if(c >= 'A' && c <= 'F')
			idx = c - 'A' + 10;
		else if(c >= 'a' && c <= 'f')
			idx = c - 'a' + 10;
		else if(c == '+')
			idx = BTN_PLUS;
		else if(c == '-')
			idx = BTN_MINUS;
		else if(c == '*')
			idx = BTN_MULT;
		else if(c == '/')
			idx = BTN_DIV;
		else if(keyCode == KeyEvent.VK_ENTER)
			idx = BTN_ENTER;
		else if(keyCode == KeyEvent.VK_BACK_SPACE || keyCode == KeyEvent.VK_DELETE)
			idx = BTN_BSP;
		else if(keyCode == KeyEvent.VK_DOWN)
			idx = BTN_POP;
		else if(keyCode == KeyEvent.VK_UP)
			idx = BTN_ENTER;
		else 
			return;

		// test if enabled
		if(button[idx].isEnabled()) {
			// fake a mouse entering over that but button
			button[idx].mouseEntered(null);
			// if it is not an out of value
			if(!button[idx].outOfRange) {
				// do like a regular call back
				callBack.buttonclick(idx);
			}
			// fake a mouse eciting event to start the shading
			button[idx].mouseExited(null);
		}
		
	}
	class Btn extends JButton implements ActionListener, MouseListener{
		// base index over which I am disable
		private static final long serialVersionUID = 1L;
		// code that I will send to the callBack routine
		private int code;
		// if the base of the GUI is higher than this value, I will be disabled
		private int baseIdx;
		// the Timer to fadeout my yellow color
		private Timer timer;
		// color index formy fadeout
		private int colorIdx;
		// if I am disabled because out of range for the word size
		// will be still enable and react to button click but won't perform call back
		private boolean outOfRange;
		
		/**
		 * Constructor for buttons that are always displayed
		 */
		private Btn(int code) {
			super(label[code][0]);
			// save code
			this.code = code;
			// if just a char make it larger
			if(label[code][0].length() == 1)
				setFont(largerFont);
			else if(code == BTN_ENTER)
				setFont(largerFont);
			setMargin(inset);
			setHorizontalAlignment(SwingConstants.CENTER);
			setBackground(Color.WHITE);			
			setForeground(Color.BLUE);
			// check for unary operator green
			for(int i = 0; i < unaryOpr.length; i++) {
				if(code == unaryOpr[i]) {
					setForeground(unaryOprColor);
					break;
				}
			}
			// long only
			for(int i = 0; i < longX.length; i++) {
				if(code == longX[i]) {
					setForeground(longColor);
					break;
				}
			}
			// int only
			for(int i = 0; i < intX.length; i++) {
				if(code == intX[i]) {
					setForeground(intColor);
					break;
				}
			}
			// my timer when I fade out my color
			timer = new Timer(100, this);
			// action listener when I click the button
			addActionListener(this);
			// to trap the mouse in and out and change my color accordingly
			addMouseListener(this);
			// the / button is disabled at start time (cannot divide by 0)
			if(code == BTN_DIV)
				setEnabled(false);
			// excption we register the change sign and ABS button only available in decimal
			if(code == BTN_CHANGE_SIGN || code == BTN_ABS) {
				al.add(this);
				return;
			}
			// if not a digit button (but 0 and 1) always enabled but we have to put them
			// because they can be disabled by the MainDisplay to avoid word overlap
			if(code < BTN_TWO || code > BTN_F) 
				baseIdx = StackElement.BINARY_IDX;
			// determine base
			else if(code < BTN_HEIGHT) 
				baseIdx = StackElement.OCTAL_IDX;
			else if(code > BTN_NINE)
				baseIdx = StackElement.HEXADECIMAL_IDX;
			else  // only 8 and 9 left
				baseIdx = StackElement.DECIMAL_IDX;
			al.add(this);
			// enable only decimal at start up
			if(baseIdx == StackElement.HEXADECIMAL_IDX)
				setEnabled(false);
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			Object o = e.getSource();
			// button was click execute my callback
			if(o == this) {
				if(outOfRange) {
					master.requestFocus();
					return;
				}
				callBack.buttonclick(code);
				master.requestFocus();
				return;
			}
			// ok it is the timer
			++colorIdx;
			if(colorIdx >= color.length) {
				timer.stop();
				return;
			}
			setBackground(color[colorIdx]);
		}

		/*
		 * Declare the button to large for word size
		 */
		private void setOutOfRange() {
			outOfRange = true;
			setFont(smallFont);
		}
		/*
		 * oveload regular steEnabled to stop color shading if in progress
		 */
		@Override
		public void setEnabled(boolean state) {
			// remove out of range if I am called is to enable or disable the button
			if(outOfRange) {
				outOfRange = false;
				setFont(largerFont);
			}
			// if enabling
			if(!state) {
				// if I am fading out
				if(timer.isRunning())
					timer.stop();
				setBackground(Color.WHITE);
			}
			super.setEnabled(state);
		}
		@Override
		public void mouseEntered(MouseEvent arg0) {
			// check if this button has tooltip (enabled or not)
			if(label[code].length == 2)
				master.setHelpText(label[code][1]);
			// do nothing is the button is disabled
			if(!isEnabled())
				return;
			// stop the timer if it was on
			timer.stop();
			// set the color to yellow
			setBackground(color[0]);
		}

		@Override
		public void mouseExited(MouseEvent arg0) {
			master.setHelpText(null);
			// do nothing is the button is disabled
			if(!isEnabled())
				return;
			// reset the color for fading out
			colorIdx = 0;
			timer.start();
		}


		public void mouseClicked(MouseEvent arg0) {}
		public void mousePressed(MouseEvent arg0) {}
		public void mouseReleased(MouseEvent arg0) {}
	}
}



RpnStack.java

import java.util.*;


/*
 * Holds the 5 RPN stack in 5 ArrayList (one for each word size) and keep them synchronized 
 */
public class RpnStack {

	// number of element to keep int the stack
	private static final int SIZE = 10;
	
	// an element at 0 use you fill it at init time and whatever all the SIZE slots are not used
	// (the base parameter is really not important here)
	private static final StackElement ZERO = new StackElement("0", StackElement.DECIMAL_IDX, StackElement.SIZE_INT);
	
	// the arrayList that contain our Stack
	private ArrayList<StackElement> al;
	// the panel that display my data
	private StackPanel panel;
	
	// constructor
	protected RpnStack(CalculatorGUI father) {
		// build the ArrayList with it's size
		al = new ArrayList<StackElement>(SIZE);
		// fill it with elements initialize to 0
		for(int i = 0; i < SIZE; ++i)
			al.add(ZERO);
		// build the panel to display my data
		panel = new StackPanel(this, father);
	}
	
	// to retreive the first element of the stack
	protected final StackElement pop() {
		StackElement se = al.remove(0);
		// if less size less than SIZE we pop too much so add a new one at 0
		if(al.size() < SIZE)
			al.add(ZERO);
		// on refresh
		panel.refresh();
		return se;
	}
	
	// to push a StackElement on the stack all the other ones are moved one up
	protected void push(StackElement se) {
		al.add(0, se);
		panel.refresh();
	}
	
	// returns an element
	protected StackElement get(int i) {
		return al.get(i);
	}
	// returns the size of the stack
	protected int getSize() {
		return SIZE;
	}
	
	// for the Gui to display my panel
	StackPanel getPanel() {
		return panel;
	}
}



SecondDisplay.java

import java.awt.*;
import java.text.DecimalFormat;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/*
 * A panel that display on 5 rows:
 * - Hexa/ASCII
 *   0/1) 
 *   2) the hex representation
 *   3) bit number
 * - Decimal
 *   0) the 2 labels Min and max
 *   1) the these 2 values
 *   2/3) the decimal value
 * - Binary
 *   0) the representation bit 63-32
 *   1) bit number
 *   2) the representation bit 31-0
 *   3) bit numbers
 *   
 */
public class SecondDisplay extends JTabbedPane implements ChangeListener {

	private static final long serialVersionUID = 1L;
	private static final Color back = new Color(225, 225, 235);
	
	// fonts at 80% and 90% used many placein my subclasses
	protected static final Font font80, font90;
	// build the 2 fonts
	static {
		// generate dummy label to get its size
		JLabel label = new JLabel();
		Font font = label.getFont();
		float size = font.getSize2D();
		// derive the 2 other ones
		font80 = font.deriveFont(size * 0.8f);
		font90 = font.deriveFont(size * 0.9f);
	}

	
	// my father to give it back focus
	private JFrame father;
	
	// my tabs
	private HexaPanel hexaPanel;
	private DecimalPanel decimalPanel;
	private BinaryPanel binaryPanel;
	// their labels
	private static final String[] tabName = {"Binary", "Hexa/Ascii", "Decimal"};

	
	/*
	 * Constructor
	 */
	SecondDisplay(JFrame father) {
		super(JTabbedPane.BOTTOM);          // multiple of 2 and 3 good also for hexa and bin representation
		this.father = father;
		// common background
		setBackground(back);
		// common border
//		setBorder(BorderFactory.createLineBorder(Color.BLUE, 1));
			
		// build the gui element of each tab
		hexaPanel = new HexaPanel();
		decimalPanel = new DecimalPanel();
		binaryPanel = new BinaryPanel();
		// add them
		addTab("Binary", binaryPanel);
		addTab("Hexa/Ascii", hexaPanel);
		addTab("Decimal", decimalPanel);
		// The Tabs 
		for(int i = 0; i < tabName.length; ++i) {
			JLabel label = new JLabel(tabName[i]);
			label.setFont(font80);
			label.setForeground(Color.BLUE);
			setTabComponentAt(i, label);
		}
		
		addChangeListener(this);
		// init for the first time
		refresh(StackElement.SIZE_INT, "0", "0", 0L);
	}
	
	
	// to update
	protected void refresh(int wordSize, String binStr, String hexaStr, long value) {
		// all my array based on word size are indexed stated a 0 for wordSize 1 which is long
		binaryPanel.updateBin(binStr, wordSize - 1);
		hexaPanel.updateHexa(hexaStr, value, wordSize - 1);
		decimalPanel.updateDecimal(wordSize - 1, value);
	}


	// give back focus to father
	public void stateChanged(ChangeEvent e) {
		father.requestFocus();
		
	}
	
	/*
	 * For Hexa display
	 */
	class HexaPanel extends VariableGridPanel {
		
		private static final long serialVersionUID = 1L;

		private final JLabel[] hexaLabel = new JLabel[8];
		private final JLabel[] asciiLabel = new JLabel[hexaLabel.length];
		private JLabel[] hexaBit;					// the bits number
		private final int[] hexaBitToHide = {0, 2, 3, 3}; 
		// for ascii word length to break in ascii translation loop
		private final int[] breakAt = {8, 4, 2, 1};
		private final JLabel asciiHeader = new JLabel("Ascii:");

		HexaPanel() {
			super(4, 24);
			setBackground(back);
			String[] bitNumberStr = {"[48]", "[32]", "[16]", "[00]"};
			
			// the bit number
			int col = 6;        // starting column
			hexaBit = new JLabel[bitNumberStr.length];
			for(int i = 0; i < bitNumberStr.length; i++) {
				// right justified using
				hexaBit[i] = new JLabel(bitNumberStr[i]);
				hexaBit[i].setHorizontalAlignment(SwingConstants.RIGHT);	
				hexaBit[i].setForeground(Color.DARK_GRAY);
				hexaBit[i].setFont(font80);
				// add to panel 9 cases width
				addComp(hexaBit[i], 1, col, 2, 1);
				col += 4;
				// begin second part
				if(i == 1) {
					col += 1;        // and restart a col 0
				}
			}
			
			// the bit labels put in reverse order
			col = 4;
			for(int i = hexaLabel.length - 1; i >= 0; i--) {
				hexaLabel[i] = new JLabel("0" + i);
				hexaLabel[i].setHorizontalAlignment(SwingConstants.RIGHT);
				hexaLabel[i].setForeground(Color.BLUE);
				addComp(hexaLabel[i], 0, col, 2, 1);
				col += 2;
				// gap between the 2 int
				if(i == 4)
					col += 1;
			}
			
			// the ascii label
			col = 15;
			for(int i = asciiLabel.length - 1; i >= 0; i--) {
				asciiLabel[i] = new JLabel("" + i);
				asciiLabel[i].setHorizontalAlignment(SwingConstants.CENTER);
				asciiLabel[i].setForeground(Color.BLUE);
				addComp(asciiLabel[i], 2, col, 1, 2);
				++col;
			}
			// a little Header
			asciiHeader.setFont(font80);
			addComp(asciiHeader, 2, 10, 4, 2);
		}
		
		// update from the main display for hexa
		private void updateHexa(String hexa, long value, int wordSize) {
			// garanty an even number to fullfill a byte
			int ilen = hexa.length();
			if(ilen % 2 == 1) {
				hexa = "0" + hexa;
				++ilen;
			}
			// make the bit number not visible
			for(int i = 0; i < hexaBit.length - 1; ++i)
				hexaBit[i].setVisible(false);
			// now the digits themselves
			char[] hexDigit = hexa.toCharArray();
			
			// put to " " all labels
			for(int i = 0; i < hexaLabel.length; ++i)
				hexaLabel[i].setText("");
			// put the set bits
	        int index = ilen / 2;
			for(int i = 0; i < ilen - 1; i += 2) {
				hexaLabel[--index].setText("" + hexDigit[i] + hexDigit[i+1]);
			}
			// make the bit number visible or not
			int i;
			for(i = 0; i < hexaBitToHide[wordSize]; ++i)
				hexaBit[i].setVisible(false);
			for(; i < hexaBit.length; ++i)
				hexaBit[i].setVisible(true);

			
			// the ascii representation
			for(i = 0; i < breakAt[wordSize]; ++i) {
				// isolate byte
				char c = (char) (value & 0xFFL);
				if(c < ' ')
					asciiLabel[i].setText(".");
				else
					asciiLabel[i].setText(Character.toString(c));
				value >>= 8;
			}
			// erase ascii value not used by this word size
			for(; i < asciiLabel.length; ++i)
				asciiLabel[i].setText("");
				
		}

	}
	
	/*
	 * For Decimal display
	 */
	class DecimalPanel extends VariableGridPanel {
		
		private static final long serialVersionUID = 1L;

		// The Header
		private JLabel decimalHeader;
		// the labels for Min and Max
		private JLabel minDec, maxDec;
		// the Strings for Min and Max depending of the base
		private String[] strMinDec, strMaxDec;	
		// the decimalFormat to format the String
		private DecimalFormat df;
		// the jLabel for the number from the calculator
		private JLabel decimalLabel;	
		// the last display WordSize just to avoid refreshing the min/max labels at each call
		private int lastWordSize = -1;
		
		DecimalPanel() {
			super(3, 2);
			setBackground(back);
			// the decimal format to format the min, max and the calculator value (large enough for Long)
			df = new DecimalFormat("###,###,###,###,###,###,##0");
			
			// build the String representation that will be displayed in the JLabel for min and max
	        strMinDec = new String[4];
	        strMaxDec = new String[4];
	        for(int i = 0; i < strMinDec.length; ++i) {
	        	strMinDec[i] = df.format(StackElement.getMinValue(i + 1));   // i+1 we skip BigInteger
	        	strMaxDec[i] = "+" + df.format(StackElement.getMaxValue(i + 1));   // i+1 we skip BigInteger       	
	        }
	        
	        // the JLabel "Decimal Min" and DecimalMax
	        decimalHeader = new JLabel("Min and Max decimal values");
	        decimalHeader.setForeground(Color.BLUE);
	        decimalHeader.setHorizontalAlignment(SwingConstants.CENTER);
	        addComp(decimalHeader, 0, 0, 2, 1);
	        
	        // the 2 labels for min and max
	        minDec = new JLabel("");
	        minDec.setHorizontalAlignment(SwingConstants.CENTER);
	        minDec.setFont(font90);
	        addComp(minDec, 1, 0);
	        maxDec = new JLabel("");
	        maxDec.setHorizontalAlignment(SwingConstants.CENTER);
	        maxDec.setFont(font90);
	        addComp(maxDec, 1, 1);
	        
	        // the display for calculator value
	        decimalLabel = new JLabel();
	        decimalLabel.setForeground(Color.BLUE);
	        decimalLabel.setHorizontalAlignment(SwingConstants.RIGHT);
	        addComp(decimalLabel, 2, 0, 2, 1);
		}
		
		// to update the decimal panel at run time
		void updateDecimal(int wordSize, long value) {
			// the JLabel with the DecimalFormat
			decimalLabel.setText(df.format(value) + "       ");
			
			// update min-max label only if required
			if(wordSize == lastWordSize)
				return;
			
			// update min and max value according to word size
			lastWordSize = wordSize;
			minDec.setText(strMinDec[wordSize]);
			maxDec.setText(strMaxDec[wordSize]);		
		}

	}
	/*
	 * For binary display
	 */
	class BinaryPanel extends VariableGridPanel {

		private static final long serialVersionUID = 1L;

		String[] bitNumberStr = {"[56]", "[48]", "[40]", "[32]", "[24]", "[16]", "[08]", "[00]"};
		private final JLabel[] bitLabel = new JLabel[64];
		private final JLabel[] binBit = new JLabel[8];
		private final int[] binBitToHide = {0, 4, 6, 7}; 

		BinaryPanel() {
			super(4, 41);
			setBackground(back);
			
			// the bit number and their value
			int col = 1;        // starting column
			int row = 1;        // starting row
			for(int i = 0; i < bitNumberStr.length; i++) {
				// right justified using 8 bits + the space between the 2 quatuor 1111 1111
				binBit[i] = new JLabel(bitNumberStr[i]);
				binBit[i].setHorizontalAlignment(SwingConstants.RIGHT);	
				binBit[i].setForeground(Color.DARK_GRAY);
				binBit[i].setFont(font80);
				// add to panel 9 cases width
				addComp(binBit[i], row, col, 10, 1);
				col += 10;
				// begin second line
				if(i == 3) {
					row = 3;
					col = 1;        // and restart at first col
				}
			}
			
			// the bit labels put in reverse order
			col = 1;
			row = 0;
			int k = 0;
			for(int i = bitLabel.length - 1; i >= 0; i--) {
				if(k++ % 4 == 0)
					++col;
				bitLabel[i] = new JLabel("");
				bitLabel[i].setFont(font90);
				bitLabel[i].setHorizontalAlignment(SwingConstants.RIGHT);
				bitLabel[i].setForeground(Color.BLUE);
				addComp(bitLabel[i], row, col);
				col++;
				// change line a bit 32
				if(i == 32) {
					row = 2;
					col = 1;   // and restart from the beginning
					k = 0;
				}
			}
		}
		
		// to update the display
		void updateBin(String binString, int wordSize) {

			// translate into char[] we can build String from it
			char[] digit = binString.toCharArray();
			int k = 0;
			for(int i = digit.length - 1; i >= 0; --i) {
				bitLabel[k++].setText("" + digit[i]);
			}
			// fill the other ones as blank
			for(; k < bitLabel.length; ++k) {
				bitLabel[k].setText("");
			}
			
			// make the bit number visible or not
			int i;
			for(i = 0; i < binBitToHide[wordSize]; ++i) 
				binBit[i].setVisible(false);
			for(; i < binBit.length; ++i)
				binBit[i].setVisible(true);

		}

	}
}



StackElement.java

import java.math.BigInteger;

/** 
 * Contains the 5 elements that are kept in the stack
 */
public class StackElement {
	
	static public final int HEXADECIMAL_IDX = 0, DECIMAL_IDX = 1, OCTAL_IDX = 2, BINARY_IDX = 3;
	static public final int SIZE_BIG = 0, SIZE_LONG = 1, SIZE_INT = 2, SIZE_SHORT = 3, SIZE_BYTE = 4; 
	private static final int baseList[] = {16, 10, 8, 2};
	
	// format because iInteger.toString, val, base) returns a minus sign - for Hex and Octal
	private static final String[] format = {"%X", "%d", "%o"};
	
	// the smallest and largest in long does not apply to BigInteger 
	private static final long min[] = {Long.MIN_VALUE, Integer.MIN_VALUE, Short.MIN_VALUE, Byte.MIN_VALUE};
	private static final long max[] = {Long.MAX_VALUE, Integer.MAX_VALUE, Short.MAX_VALUE, Byte.MAX_VALUE};
	
	// for octal conversion
	static private final char[] ascii = {'0', '1', '2', '3', '4', '5', '6', '7'};
	// the element in the 5 base
	private BigInteger bigValue;
	private long longValue;
	private int intValue;
	private short shortValue;
	private byte byteValue;
	
	// constructor receives a String (most often from the JLabel) and the actual base and wordSize
	protected StackElement(String label, int base, int wordSize) {
		setValue(label, base, wordSize);
	}
	
	// to clone a StackElement
	protected StackElement (StackElement se) {
		bigValue = new BigInteger(se.bigValue.toString());
		longValue = se.longValue;
		intValue = se.intValue;
		shortValue = se.shortValue;
		byteValue = se.byteValue;
	}
	
	// when changing a value or called by the constructor 
	protected void setValue(String label, int base, int wordSize) {
//		System.out.println("SetValue Str: >" + label + "< base: " + base + ">" + baseList[base]);
		// parse it anyhow as a BigInteger to avoid java expecting - sign for Hex/oct/bin
		bigValue = new BigInteger(label, baseList[base]);
		
		// convert from bigger to smaller
        longValue = bigValue.longValue();
		intValue = (int) longValue;
		shortValue = (short) intValue;
		byteValue = (byte) shortValue;
		
		// then promote from smaller to bigger
		switch(wordSize) {
			case SIZE_BYTE:
				longValue = intValue = shortValue = byteValue;
				break;
			case SIZE_SHORT:
				longValue = intValue = shortValue;
				break;
			case SIZE_INT:
				longValue = intValue;
				break;
		}	
//		System.out.println("> " + label + "< L " + longValue + " I " + intValue + " S " + shortValue + " B " + 

byteValue);
	}
	
	// returns the String representation based on size and base
	protected String toString(int sizeIdx, int baseIdx) {
        // for Short and Byte the hex string of intValue
		String str;
		// and it's len
		int ilen;
		// index of first not 0 when in binary
		int index;
		
		// depending of wordSize
		switch(sizeIdx) {
			case SIZE_BIG:
				str = bigValue.toString(baseList[baseIdx]);
				if(baseIdx == HEXADECIMAL_IDX)
					return str.toUpperCase();
				return str;
	            
			case SIZE_LONG:
				if(baseIdx == BINARY_IDX) 
					return Long.toBinaryString(longValue);						
				else
					return String.format(format[baseIdx], longValue);					

	

			case SIZE_INT:
				if(baseIdx == BINARY_IDX) 
					return Integer.toBinaryString(intValue);						
				else
					return String.format(format[baseIdx], intValue);					

	
				
			case SIZE_SHORT:
				switch(baseIdx) {
					case HEXADECIMAL_IDX:
						str = String.format("%X", intValue);
						ilen = str.length();
						if(ilen > 4)
							str = str.substring(ilen - 4);
						return str;
					case DECIMAL_IDX:
						return String.format("%d", shortValue);
					case OCTAL_IDX:
						return shortToOctalString();
					case BINARY_IDX: 
						str = Integer.toBinaryString(intValue & 0xFFFF);
						// get rid of leadind 0
						index = str.indexOf('1');
						if(index == -1)
							return "0";
						else
							return str.substring(index);
				}
				
			case SIZE_BYTE:
				switch(baseIdx) {
					case HEXADECIMAL_IDX:
						str = String.format("%X", intValue);
						ilen = str.length();
						if(ilen > 2)
							str = str.substring(ilen - 2);
						return str;
					case DECIMAL_IDX:
						return String.format("%d", byteValue);
					case OCTAL_IDX:
						return byteToOctalString();
					case BINARY_IDX: 
						str = Integer.toBinaryString(intValue & 0xFF);					
						// get rid of leadind 0
						index = str.indexOf('1');
						if(index == -1)
							return "0";
						else
							return str.substring(index);
				}

		} // end switch(sizeIdx)
		throw new IllegalStateException("Shouldn't pass here");
	}
	
	// return min and max value for a size
	static long getMinValue(int sizeIdx) {
		return min[sizeIdx - 1];
	}
	// return min and max value for a size
	static long getMaxValue(int sizeIdx) {
		return max[sizeIdx - 1];
	}

    /**
     * the getters
     */
	protected final BigInteger getBigInteger() {
		return bigValue;
	}
	protected final long getLong() {
		return longValue;
	}
	protected final int getInt() {
		return intValue;
	}
	protected final short getShort() {
		return shortValue;
	}
	protected final byte getByte() {
		return byteValue;
	}
	
	/*
	 * Octal conversion more complicated than the others because not on byte boundary
	 * we cannot i & 0xFF or i & 0xFFFF as we did for binaty or hexa
	 */
	private String byteToOctalString() {
		// to stor the octal digit as we found them
		char[] digit = new char[3];
		// index to digit[] as we go backward
		int digitIdx = digit.length;
		// in int (would be promoted anyway)
		int value = intValue;
		// to store twice the 3 first bits and the 2 last one
		int j;
		
		// the 2 first 3 bits
		for(int i = 0; i < 2; i++) {
			j = value & 0x7;
			digit[--digitIdx] = ascii[j];
			value >>= 3;
		}
		// the last 2 bits
		j = value & 0x3;
		digit[0] = ascii[j];
		
		return new String(digit);
	}
	private String shortToOctalString() {
		// to stor the octal digit as we found them
		char[] digit = new char[6];
		// index to digit[] as we go backward
		int digitIdx = digit.length;
		// in int (would be promoted anyway)
		int value = intValue;
		// to store 5 times the 3 first bits and the 2 last one
		int j;
		
		// the 2 first 3 bits
		for(int i = 0; i < 5; i++) {
			j = value & 0x7;
			digit[--digitIdx] = ascii[j];
			value >>= 3;
		}
		// the last single bit
		j = value & 0x1;
		digit[0] = ascii[j];
		
		return new String(digit);
	}
}



StackPanel.java

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

/**
 * To display the stack
 *
 */
public class StackPanel extends JPanel implements MouseListener {

	private static final long serialVersionUID = 1L;
	
	// color of the label
	private static final Color lightColor = new Color(255, 255, 160);
	
	// to know which stack to display
	private int baseIdx = StackElement.DECIMAL_IDX;
	// the word size
	private int sizeIdx = StackElement.SIZE_INT;

	// who to call back for the values
	private RpnStack rpnStack;
	// the JLabel displaying the stack
	private JLabel[] label;
	// the master frame for help callback
	private CalculatorGUI father;

	/**
	 * Constructor
	 */
	protected StackPanel(RpnStack rpnStack, CalculatorGUI father) {
		
		super(new BorderLayout());
		this.father = father;
        setBorder(BorderFactory.createLineBorder(Color.BLACK));
		// save my stack
		this.rpnStack = rpnStack;
		int size = rpnStack.getSize();

		// a Header in the NorthRegion
		JLabel head = new JLabel("Stack");
		head.setHorizontalAlignment(SwingConstants.CENTER);
		head.setOpaque(true);
		head.setBackground(Color.YELLOW);
		add(head, BorderLayout.NORTH);
		
		// on the EastRegion the stack depth
		JPanel layoutPanel = new JPanel(new GridLayout(size, 1));
		// the labels of the depth
		for(int i = size - 1; i >= 0; --i) {
			JLabel label = new JLabel();
			// we call the first stack element Y
			if(i == 0) {
				label.setText(" Y ");
			}
			else {
				label.setText(" " + i + " ");
			}
			label.setBorder(BorderFactory.createLineBorder(Color.BLUE));
			label.setOpaque(true);
			label.setBackground(lightColor);
			label.setHorizontalAlignment(SwingConstants.CENTER);
			layoutPanel.add(label);
		}
		// added to the East Region
		add(layoutPanel, BorderLayout.WEST);

		// The center column
		layoutPanel = new JPanel(new GridLayout(size, 1));
		// the labels to display stack element
		label = new JLabel[size];
		for(int i = size-1; i >= 0; i--) {
			label[i] = new JLabel();
			label[i].setOpaque(true);
			label[i].setBackground(Color.WHITE);
			label[i].setHorizontalAlignment(SwingConstants.RIGHT);
			// for the help text or the click
			label[i].addMouseListener(this);
			JScrollPane sp = new JScrollPane(label[i]);
			sp.setBorder(BorderFactory.createLineBorder(Color.BLACK));
			layoutPanel.add(sp);
		}
		// add it center
		add(layoutPanel, BorderLayout.CENTER);	
		// load the stack for the first time
		refresh();
	}

	/**
	 * The base change
	 */
	protected void setBase(int idx)  {
		baseIdx = idx;
		// base changed the display element surely
		refresh();
	}
	/**
	 * The word size change
	 */
	protected void setWordSize(int idx)  {
		sizeIdx = idx;
		// base changed the display element surely
		refresh();
	}

	/*
	 * Refresh required, the stack changed
	 */
	protected void refresh() {
		// we update our labels
		for(int i = 0; i < label.length; ++i) {
			// the stack element to display
			StackElement se = rpnStack.get(i);
            // according to word size and base
			label[i].setText(se.toString(sizeIdx, baseIdx) + " ");
		}
	}


	// righ now just one JLabel has a mouse listener
	public void mouseEntered(MouseEvent e) {
		if(e.getSource() == label[0])
			father.setHelpText("The bottom of the stack which is the first operand of all operations performed. 

Named register Y in the help messages.");
		else
			father.setHelpText("Click on this stack register to copy it into the X register");
	}

	// simply erase the help text
	public void mouseExited(MouseEvent arg0) {
		father.setHelpText(null);
	}
	// mouse click we have to copy into register X but for label[0]
	public void mouseClicked(MouseEvent e) {
		Object o = e.getSource();
		for(int i = 0; i < label.length; i++) {
			if(o == label[i]) {
				// the stack element to display
				father.setReg0(rpnStack.get(i));
				return;
			}
		}
	}

	// unused mouse event
	public void mousePressed(MouseEvent arg0) {}
	public void mouseReleased(MouseEvent arg0) {}
}




VariableGridPanel.java

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

/**
 * A class that implements a JPanel which is split in a grid like in a GridLayout
 * but a lot easier to use than a GridBagLayout
 * 
 * Differences with GridLayout:
 * - you can only add JComponent to it
 * - you are not force to add a component to all cells
 * - for each component you add you specify on whow many grid it should spread
 *   horizontaly and verticaly (default 1, 1) this allow a component to span over more than one cell
 * - you can add and remove component whenever you want
 * - a cell can be shared by two components (bigger than 1X1) they will just overlap
 * - when a gap is specified it also apply to the beginning and edge of the JPanel
 *  
 * I wrote it quick and dirty because I quickly need a calculator (usually ideal from GridLayout) 
 * but I need the "0" and "Enter" key to spread over 2 cells
 */
public class VariableGridPanel extends JPanel {

	private static final long serialVersionUID = 1L;
	
	// an ArrayList to hold all the JComponent added
	private ArrayList<CompPosSize> al = new ArrayList<CompPosSize>();
	// number of rows, columns and the gap in pixels between them
	private int nbRow, nbCol, xGap, yGap;
	
	/**
	 * Constructor that receives the number of row and column as parameter
	 */
	public VariableGridPanel(int row, int col) {
		// wich calls the constructor where gaps are secified
		this(row, col, 0, 0);
	}
	/**
	 * Constructor that receives the number of row, the number of columns and the gap
	 */
	public VariableGridPanel(int row, int col, int hGap, int vGap) {
		// we are using a null panel
		super(null);
		nbRow = row;
		nbCol = col;
		xGap = hGap;
		yGap = vGap;
	}
	
	// to add a JComponent with its coordinates
	public void addComp(JComponent comp, int row, int col) {
		// defaulted to 1 grid width and 1 grid height
		addComp(comp, row, col, 1, 1);
	}
	// to add a JComponent that spread over more than one cell
	public void addComp(JComponent comp, int row, int col, int w, int h) {
		// register the JComponent and its x,y,w,h in the ArrayList
		al.add(new CompPosSize(comp, row, col, w, h));
		add(comp);
	}
	
	// to remove a component
	public void remove(JComponent comp) {
        int size = al.size();
        // scan the arraylist
		for(int i = 0; i < size; i++) {
			CompPosSize cps = al.get(i);
			// if found
			if(cps.comp == comp) {
				super.remove(comp);
				al.remove(i);    // remove it
				return;			 // done
			}
		}
	}

	/**
	 * Overload the paintComponent method
	 */
	public void paintComponent(Graphics g) {
		// before calling my super.paintComponent() method
		// determine the size of each registered component
		Dimension size = getSize();
		
		int cellWidth = size.width / nbCol;
		int cellHeight = size.height / nbRow;
		for(CompPosSize cps : al) {
			JComponent comp = cps.comp;
			// if component is not visible
			if(!comp.isVisible()) {
				cps.comp.setBounds(0, 0, 0, 0);
				continue;
			}
			// the x position is x * cellWidth
			int xPos = cellWidth * cps.x + xGap;
			// the width is the cellWidth times my width in cell - gap
			int width = cellWidth * cps.w - xGap;
			// same thing on the yAxis
			int yPos = cellHeight * cps.y + yGap;
			int height = cellHeight * cps.h - yGap;
			// set the bounds of our component
			comp.setBounds(xPos, yPos, width, height);
		}
		
		// call my super to paint the components now they have been positionned
		super.paintComponent(g);
	}
	
	/*
	 * To Unit test the whole thing
	 */
	public static void main(String[] args) {
		// create a frame
		JFrame f = new JFrame("Test variable grid");
		// and a VariableGridPanel
		VariableGridPanel vgp = new VariableGridPanel(10, 10, 2, 1);
		// add different buttons for our test
		vgp.addComp(new JButton("BTN0"), 0, 0, 2, 1);
		vgp.addComp(new JButton("BTN1"), 2, 2);
		vgp.addComp(new JButton("BTN3"), 3, 1);
		vgp.addComp(new JButton("BTN4"), 3, 2);
		vgp.addComp(new JButton("BTN2"), 2, 5, 2, 3);
		// and one that will share a cell with the previous one
		vgp.addComp(new JButton("BTN5"), 4, 6, 2, 2);
		vgp.addComp(new JButton("BTN6"), 9, 9);
		// and a JLabel that I will make disappear
		JLabel label = new JLabel("I will eventually disappear :)/>");
		vgp.addComp(label, 6, 2, 4, 1);
		f.add(vgp);
		f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		// show the JFrame
		f.setSize(500, 500);
		f.setVisible(true);
		
		// wait 3 seconds
		try {
			Thread.sleep(3000);
		}
		catch(Exception e) {}
		// remove the JLabel
		vgp.remove(label);
		vgp.repaint();
	}
	
	/**
	 * A private class that holds a JComponent, its coordinates and size
	 */
	private class CompPosSize {
		JComponent comp;
		int x, y, w, h;
		
		/**
		 * Constructor
		 */
		CompPosSize(JComponent comp, int row, int col, int width, int height) {
			this.comp = comp;
			y = row;
			x = col;
			w = width;
			h = height;
		}
	}
}



WordSizeAndBasePanel.java

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


/**
 * Display the panel that permit to select the base and the size of the word
 *
 */
public class WordSizeAndBasePanel extends JPanel implements ActionListener {

	private static final long serialVersionUID = 1L;

	static private final String[] btnTextBase = {"Hexadecimal", "Decimal", "Octal", "Binary"};
	static private final String[] btnTextSize = {"BigInteger", "Long", "Integer", "Short", "Byte"};

	private JRadioButton[] radioBase = new JRadioButton[btnTextBase.length];
	private JRadioButton[] radioSize = new JRadioButton[btnTextSize.length];
	
	// the father that I will call when the base changes
	private CalculatorGUI rpn;
	/**
	 * Constructor
	 */
	WordSizeAndBasePanel(CalculatorGUI rpn, JCheckBox scroll, JCheckBox help) {
		// nb rows: 3 header + 2 JCheckBox + all JRadioButton
		super(new GridLayout(3 + 2 + btnTextBase.length + btnTextSize.length, 1, 3, 3));
		this.rpn = rpn;
		setBorder(BorderFactory.createLineBorder(Color.BLACK));

		// the base header
		JLabel label = new JLabel("--- Number Base ---");
		label.setHorizontalAlignment(SwingConstants.CENTER);
		label.setForeground(Color.BLUE);
		add(label);
		// the radioButton for the base we need a VariableGridPanel for each row to align cute
		ButtonGroup bg = new ButtonGroup();
		for(int i = 0; i < radioBase.length; i++) {
			// the RadioButton 
			radioBase[i] = new JRadioButton(btnTextBase[i]);
			radioBase[i].addActionListener(this);
			bg.add(radioBase[i]);
			add(radioBase[i]);
		}
		// set the decimal button to on
		radioBase[StackElement.DECIMAL_IDX].setSelected(true);	

		// the wordsize
		label = new JLabel("--- Word Size ---");
		label.setHorizontalAlignment(SwingConstants.CENTER);
		label.setForeground(Color.BLUE);
		add(label);
		// the radioButton for the word size
		bg = new ButtonGroup();
		for(int i = 0; i < radioSize.length; i++) {
			radioSize[i] = new JRadioButton(btnTextSize[i]);
			radioSize[i].addActionListener(this);
			bg.add(radioSize[i]);
			add(radioSize[i]);
		}
		// set the int button to on
		radioSize[StackElement.SIZE_INT].setSelected(true);	

		// the options
		label = new JLabel("--- Options ---");
		label.setHorizontalAlignment(SwingConstants.CENTER);
		label.setForeground(Color.BLUE);
		add(label);
		// the 2 checkbox
		add(scroll);
		add(help);
		
	}
	@Override
	public void actionPerformed(ActionEvent e) {
		// find wich button has been clicked
		Object but = e.getSource();
		// radio for base
		for(int i = 0; i < radioBase.length; i++) {
			if(but == radioBase[i]) {
				rpn.setBase(i);      // inform everybody the base changed
				return;
			}
		}
		// radio for word size
		for(int i = 0; i < radioSize.length; i++) {
			if(but == radioSize[i]) {
				rpn.setWordSize(i);      // inform everybody the base changed
				return;
			}
		}
	}
	
}



Was This Post Helpful? 0
  • +
  • -

#9 Curtis Rutland  Icon User is online

  • (╯□)╯︵ (~ .o.)~
  • member icon


Reputation: 4314
  • View blog
  • Posts: 7,473
  • Joined: 08-June 10

Posted 26 November 2010 - 01:07 PM

Wow. I just spent hours writing a tutorial for a RPN calc in Silverlight/C#. It came to me because of my convoluted logic...someone wanted to do a unit conversion application, and my overcomplicated solution was to store the conversion equations in postfix in an xml file. So I wrote a postfix expression parser, and figured I'd go as far as writing the calculator.

Anyway...

I just got done cleaning it up and posting it, then I went to the front page for some silly reason...and I see that you've had this posted for six days already. I guess great minds think alike...only mine goes slower.

Anway, we've taken pretty different tacks (mine is an extremely simple, non-scientific calculator, and yours looks like the whole shebang), and we're both using different languages and technology, but it's just funny to me that I'd go through all that effort, and someone else has already posted a tutorial about that on this site.

Great article by the way.

This post has been edited by insertAlias: 26 November 2010 - 01:09 PM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1