home *** CD-ROM | disk | FTP | other *** search
/ com!online 2002 May / comcd0502.iso / homepage / javaspecial / 03_01 / romanus / CompvterRomanvs.java < prev    next >
Encoding:
Java Source  |  1997-12-18  |  66.8 KB  |  1,636 lines

  1. /*-****************************                 *****************************-*/
  2. /*-**************************** CompvterRomanvs *****************************-*/
  3. /*-*                                                                        *-*/
  4. /*-*         This applet is a calculator that works in Roman Numerals       *-*/
  5. /*-*                   Copyright ⌐ MCMXCVII Edward R. Hobbs                 *-*/
  6. /*-*                                                                        *-*/
  7. /*-**************************************************************************-*/
  8. /*-**************************************************************************-*/
  9.  
  10. import java.applet.*;
  11. import java.awt.*;
  12. import java.awt.image.*;
  13.  
  14. public class CompvterRomanvs extends java.applet.Applet implements Runnable
  15.    {
  16.    /*-------------------------------------------------------------------------*/
  17.    /* These data items index the calculator keys and their associated images  */
  18.    /*-------------------------------------------------------------------------*/
  19.  
  20.    final int    m_ndxI            =  0;
  21.    final int    m_ndxV            =  1;
  22.    final int    m_ndxX            =  2;
  23.    final int    m_ndxL            =  3;
  24.    final int    m_ndxC            =  4;
  25.    final int    m_ndxD            =  5;
  26.    final int    m_ndxM            =  6;
  27.    final int    m_ndxVbar         =  7;
  28.    final int    m_ndxXbar         =  8;
  29.    final int    m_ndxLbar         =  9;
  30.    final int    m_ndxCbar         = 10;
  31.    final int    m_ndxDbar         = 11;
  32.    final int    m_ndxMbar         = 12;
  33.                                  
  34.    final int    m_ndxMaxNumeral   = 12;    /* Index of maximum numeral        */
  35.                                   
  36.    final int    m_ndxAdd          = 13;
  37.    final int    m_ndxSubtract     = 14;
  38.    final int    m_ndxMultiply     = 15;
  39.    final int    m_ndxDivide       = 16;
  40.    final int    m_ndxEquals       = 17;
  41.                                   
  42.    final int    m_ndxClear        = 18;
  43.    final int    m_ndxClearEntry   = 19;
  44.                                   
  45.    final int    m_nCalcKeys       = 20;    /* Total number of calculator keys */
  46.  
  47.  
  48.    /*-------------------------------------------------------------------------*/
  49.    /*  The following arrays hold the calculator key objects and their images. */
  50.    /*-------------------------------------------------------------------------*/
  51.  
  52.    Image[]      m_imageNumeral    = new Image[m_nCalcKeys];
  53.    Image        m_imageBackground;
  54.    Image        m_imageCaesar;
  55.  
  56.    MediaTracker m_mediaTracker    = new MediaTracker ( this );
  57.    Image        m_imageLoading    = null;
  58.                              
  59.    CalcKey[]    m_calckey         = new CalcKey[m_nCalcKeys];
  60.    CalcKey      m_calckeyPressed  = null;
  61.  
  62.    int          m_ndxLastKey      = m_ndxClear; /* Index of latest keypress   */
  63.    int          m_ndxPrevOperator = m_ndxAdd;   /* Index of previous operator */
  64.  
  65.  
  66.    /*-------------------------------------------------------------------------*/
  67.    /* The following data items are used to keep track of the current display  */
  68.    /*-------------------------------------------------------------------------*/
  69.  
  70.    final int    m_nDisplayX0      =  15;
  71.    final int    m_nDisplayY0      =  15;
  72.    final int    m_nDisplayWidth   = 550;
  73.    final int    m_nDisplayHeight  =  35;
  74.    final int    m_nDisplayGap     =   1;
  75.    final int    m_nDisplayMargin  =   5;
  76.                                   
  77.    final int    m_nMaxNumerals    =  27;
  78.    int          m_nNumerals       =   0;
  79.    int[]        m_nNumeral        = new int[m_nMaxNumerals];
  80.                                   
  81.    final Color  m_colorDisplay    = new Color ( 192, 192, 192 );
  82.    Image        m_imageDisplay;   
  83.                 
  84.    final int    m_nBezelWidth     = 2;
  85.    final Color  m_colorBezelLight = new Color ( 255, 255, 255 );
  86.    final Color  m_colorBezelDark  = new Color (  80,  80,  80 );
  87.  
  88.  
  89.    /*-------------------------------------------------------------------------*/
  90.    /*                        Miscellaneous data items                         */
  91.    /*-------------------------------------------------------------------------*/
  92.  
  93.    final int m_nMaxValue    = 3999999;  /* Range of values that can be...     */
  94.    final int m_nMinValue    =       1;  /*     ...expressed as Roman Numerals */
  95.                                         
  96.    int       m_nError       =       0;  /* See values below                   */
  97.    boolean   m_bErrorPhase  =    true;  /* Updated by background thread       */
  98.                             
  99.    final int m_nOVERFLOW    =      -1;  /* Value too big for Roman Numerals   */
  100.    final int m_nUNDERFLOW   =      -2;  /* Value too small for Roman Numerals */
  101.    final int m_nINVALID     =      -3;  /* Invalid sequence of Numerals       */
  102.                                         
  103.    int       m_nAccumulator ;           /* Calculator logical accumulator     */
  104.                             
  105.    Thread    m_thread       =    null;
  106.    boolean   m_bInitialized =   false;
  107.  
  108.    int[]     m_nKeyBuffer   = new int[4];        /* Buffer for "INFO" command */
  109.  
  110.    final int m_nFrameWidth     = 5;
  111.    Color     m_colorFrameLight = new Color ( 255, 255, 255 );
  112.    Color     m_colorFrameDark  = new Color (  80,  80,  80 );
  113.    
  114.  
  115.     /*-******************************** init *********************************-*/
  116.    /*                                                                         */
  117.    /*    This method is invoked exactly once, the first time the applet is    */
  118.    /*    started.                                                             */
  119.    /*                                                                         */
  120.      /*-***********************************************************************-*/
  121.   
  122.    public void init()
  123.       {
  124.       start();
  125.       }
  126.  
  127.  
  128.      /*-****************************** keyDown ********************************-*/
  129.    /*                                                                         */
  130.    /* Intercept keystokes destined for the applet.  When the "INFO" sting is  */
  131.    /* typed (case sensitive!), repaint the window with a copyright notice.    */
  132.    /* This allows the applet to be positively identified from a web browser.  */
  133.    /*                                                                         */
  134.      /*-***********************************************************************-*/
  135.  
  136.    public boolean keyDown ( Event evt, int nKey )
  137.       {
  138.       m_nKeyBuffer[3] = m_nKeyBuffer[2];         /*---------------------------*/
  139.       m_nKeyBuffer[2] = m_nKeyBuffer[1];         /* As each key is received,  */
  140.       m_nKeyBuffer[1] = m_nKeyBuffer[0];         /* shift it into the buffer. */
  141.       m_nKeyBuffer[0] = nKey;                    /*---------------------------*/
  142.  
  143.       if ( ( m_nKeyBuffer[3] == (int) 'I' ) &&   /*---------------------------*/
  144.            ( m_nKeyBuffer[2] == (int) 'N' ) &&   /* Check for the "INFO"      */
  145.            ( m_nKeyBuffer[1] == (int) 'F' ) &&   /* string.                   */
  146.            ( m_nKeyBuffer[0] == (int) 'O' ) )    /*---------------------------*/
  147.          {
  148.          stop();       /* We don't want to collide with the background thread */   
  149.  
  150.          Graphics     g      = getGraphics();
  151.          FontMetrics  fm     = g.getFontMetrics();
  152.          Dimension    dimWin = size();
  153.  
  154.          g.setColor   ( Color.white );
  155.          g.fillRect   ( 0, 0, dimWin.width, dimWin.height );
  156.  
  157.          g.setColor   ( Color.black );
  158.  
  159.          /*-------------------------------------------------------------------*/
  160.          /* This is the identifying info displayed in response to the "INFO"  */
  161.          /* command.                                                          */
  162.          /*-------------------------------------------------------------------*/
  163.  
  164.          g.drawString ( "COMPVTER ROMANVS",         20,     fm.getHeight() );
  165.          g.drawString ( "(c) 1997 Edward R. Hobbs", 20, 3 * fm.getHeight() );
  166.          g.drawString ( "    edhobbs@aol.com",      20, 4 * fm.getHeight() );
  167.          g.drawString ( "    All rights reserved.", 20, 5 * fm.getHeight() );
  168.          }
  169.  
  170.       return true;
  171.       }
  172.  
  173.  
  174.     /*-******************************** paint ********************************-*/
  175.    /*                                                                         */
  176.     /*-***********************************************************************-*/
  177.  
  178.    public void paint ( Graphics g )
  179.       {
  180.       Dimension   dimWin = size();
  181.  
  182.         if ( ! m_bInitialized )
  183.          {
  184.          /*-------------------------------------------------------------------*/
  185.          /* During initialization, the background thread paints the applet    */
  186.          /* window with a bar graph showing progress in loading graphics.     */
  187.          /*-------------------------------------------------------------------*/
  188.  
  189.          return;
  190.          }
  191.       else
  192.          {
  193.          /*-*****************************************************************-*/
  194.          /*     If the applet has been initialized, redraw the calculator.    */
  195.          /*-*****************************************************************-*/
  196.  
  197.          int nWidth  = m_imageBackground.getWidth  ( null );
  198.          int nHeight = m_imageBackground.getHeight ( null );
  199.  
  200.          for ( int nX = 0; nX < dimWin.width; nX += nWidth )
  201.             for ( int nY = 0; nY < dimWin.height; nY += nHeight )
  202.                g.drawImage ( m_imageBackground, nX, nY, null );
  203.  
  204.          g.drawImage ( m_imageCaesar,  90, 70, null );
  205.  
  206.  
  207.          /*-------------------------------------------------------------------*/
  208.          /*     Draw the beveled frame around the edge of the calculator      */
  209.          /*-------------------------------------------------------------------*/
  210.  
  211.          g.setColor ( m_colorFrameLight );
  212.  
  213.          for ( int i = 0; i < m_nFrameWidth; i++ )
  214.             {
  215.             g.drawLine ( i, i, dimWin.width - 1 - i, i );
  216.             g.drawLine ( i, i, i, dimWin.height - 1 - i );
  217.             }
  218.  
  219.          g.setColor ( m_colorFrameDark );
  220.  
  221.          for ( int i = 0; i < m_nFrameWidth; i++ )
  222.             {
  223.             g.drawLine ( dimWin.width - 1 - i, dimWin.height - 1 - i, i, dimWin.height - 1 - i ); 
  224.             g.drawLine ( dimWin.width - 1 - i, dimWin.height - 1 - i, dimWin.width - 1 - i, i ); 
  225.             }
  226.  
  227.  
  228.          /*-------------------------------------------------------------------*/
  229.          /*           Draw the bezel around the edge of the display           */
  230.          /*-------------------------------------------------------------------*/
  231.  
  232.          g.setColor ( m_colorBezelDark );
  233.  
  234.          for ( int i = 0; i < m_nBezelWidth; i++ )
  235.             {
  236.             g.drawLine ( m_nDisplayX0 - i - 1, 
  237.                          m_nDisplayY0 - i - 1, 
  238.                          m_nDisplayX0 + m_nDisplayWidth + i,
  239.                          m_nDisplayY0 - i - 1 );
  240.  
  241.             g.drawLine ( m_nDisplayX0 - i - 1, 
  242.                          m_nDisplayY0 - i - 1, 
  243.                          m_nDisplayX0 - i - 1,
  244.                          m_nDisplayY0 + m_nDisplayHeight + i );
  245.             }
  246.  
  247.          g.setColor ( m_colorBezelLight );
  248.  
  249.          for ( int i = 0; i < m_nBezelWidth; i++ )
  250.             {
  251.             g.drawLine ( m_nDisplayX0 + m_nDisplayWidth  + i, 
  252.                          m_nDisplayY0 + m_nDisplayHeight + i, 
  253.                          m_nDisplayX0 + m_nDisplayWidth  + i,
  254.                          m_nDisplayY0 - i - 1 );
  255.  
  256.             g.drawLine ( m_nDisplayX0 + m_nDisplayWidth  + i, 
  257.                          m_nDisplayY0 + m_nDisplayHeight + i, 
  258.                          m_nDisplayX0 - i - 1,
  259.                          m_nDisplayY0 + m_nDisplayHeight + i );
  260.             }
  261.  
  262.  
  263.          for ( int i = 0; i < m_nCalcKeys; i++ )
  264.             m_calckey[i].Paint ( g, this );
  265.  
  266.          drawDisplay ( g );
  267.          }
  268.       }
  269.  
  270.  
  271.     /*-****************************** mouseDown ******************************-*/
  272.    /*                                                                         */
  273.    /* When the user presses the mouse button, check to see if the user clicked*/
  274.    /* on one of the calculator keys.                                          */
  275.    /*                                                                         */
  276.     /*-***********************************************************************-*/
  277.  
  278.    public boolean mouseDown ( Event evt, int nX, int nY )
  279.       {
  280.       if ( ! m_bInitialized ) return true;
  281.  
  282.       for ( int i = 0; i < m_nCalcKeys; i++ )
  283.          {
  284.          if ( m_calckey[i].testHit ( nX, nY ) )
  285.             if ( m_calckey[i] != m_calckeyPressed )
  286.                {
  287.                /*-------------------------------------------------------------*/
  288.                /* If one of the calculator keys is already pressed (and it is */
  289.                /* not this key), then un-press it.                            */
  290.                /*-------------------------------------------------------------*/
  291.  
  292.                if ( m_calckeyPressed != null )
  293.                   {
  294.                   m_calckeyPressed.setPressed ( false, this );
  295.                   m_calckeyPressed = null;
  296.                   }
  297.  
  298.                /*-------------------------------------------------------------*/
  299.                /*           Redraw the newly-pressed calculator key.          */
  300.                /*-------------------------------------------------------------*/
  301.  
  302.                m_calckey[i].setPressed ( true, this );
  303.                m_calckeyPressed = m_calckey[i];
  304.                                                    /*-------------------------*/
  305.                handleCalcKeyPress ( i );           /* Respond to the keypress */
  306.                                                    /*-------------------------*/
  307.                break;
  308.                }
  309.          }
  310.  
  311.       return true;
  312.       }
  313.  
  314.  
  315.     /*-******************************* Mouse Up ******************************-*/
  316.    /*                                                                         */
  317.    /* When the user releases the mouse button, raise any calculator key that  */
  318.    /* happens to be pressed.                                                  */
  319.    /*                                                                         */
  320.    /*-***********************************************************************-*/
  321.  
  322.    public boolean mouseUp ( Event evt, int nX, int nY )
  323.       {
  324.       if ( m_calckeyPressed != null )                  
  325.          {                                             
  326.          m_calckeyPressed.setPressed ( false, this );  
  327.          m_calckeyPressed = null;                      
  328.          }
  329.          
  330.       return true;                                                
  331.       }
  332.  
  333.  
  334.     /*-***************************** Mouse Drag ******************************-*/
  335.    /*                                                                         */
  336.    /* Even if the user holds the mouse button down, raise the pressed key if  */
  337.    /* he drags the mouse out of the area the key covers.                      */
  338.    /*                                                                         */
  339.     /*-***********************************************************************-*/
  340.  
  341.    public boolean mouseDrag ( Event evt, int nX, int nY )
  342.       {
  343.       if ( m_calckeyPressed != null )
  344.          if ( ! m_calckeyPressed.testHit ( nX, nY ) )
  345.             {                                             
  346.             m_calckeyPressed.setPressed ( false, this );  
  347.             m_calckeyPressed = null;                      
  348.             }
  349.          
  350.       return true;                                                
  351.       }
  352.  
  353.  
  354.  
  355.     /*-************************** handleCalcKeyPress *************************-*/
  356.    /*                                                                         */
  357.    /* This function performs the logical operation associated with the key    */
  358.    /* which has just been pressed.                                            */
  359.    /*                                                                         */
  360.     /*-***********************************************************************-*/
  361.  
  362.    private void handleCalcKeyPress ( int ndxCalcKey )
  363.       {
  364.       /*----------------------------------------------------------------------*/
  365.       /* If there is an outstanding error code, then ignore any keypress      */
  366.       /* except Clear.                                                        */
  367.       /*----------------------------------------------------------------------*/
  368.  
  369.       if ( m_nError != 0 )
  370.          {
  371.          if ( ndxCalcKey == m_ndxClear )
  372.             {
  373.             m_nError          = 0;
  374.             m_nNumerals       = 0;
  375.             m_nAccumulator    = 0;
  376.             m_ndxLastKey      = m_ndxClear;
  377.             m_ndxPrevOperator = m_ndxAdd;
  378.  
  379.             updateDisplay();
  380.             }
  381.  
  382.          return;
  383.          }
  384.  
  385.  
  386.       /*----------------------------------------------------------------------*/
  387.       /*                    The user pressed a numeral key                    */
  388.       /*----------------------------------------------------------------------*/
  389.  
  390.       if ( ndxCalcKey <= m_ndxMaxNumeral )
  391.          {
  392.          if ( m_ndxLastKey <= m_ndxMaxNumeral )
  393.             {
  394.             /*----------------------------------------------------------------*/
  395.             /* If the previous key was also a numeral, then append this key   */
  396.             /* to the string of numerals in the display.                      */
  397.             /*----------------------------------------------------------------*/
  398.  
  399.             if ( m_nNumerals >= m_nMaxNumerals )  /*--------------------------*/
  400.                {                                  /*  If the user put in too  */
  401.                m_nAccumulator    = 0;             /*  many numerals, set the  */
  402.                m_nNumerals       = 0;             /*  error code to INVALID.  */
  403.                m_nError          = m_nINVALID;    /*--------------------------*/
  404.                m_ndxPrevOperator = m_ndxAdd;
  405.                }
  406.             else
  407.                {
  408.                m_nNumeral[m_nNumerals] = ndxCalcKey;
  409.                m_nNumerals++;
  410.                }
  411.             }
  412.          else
  413.             {
  414.             /*----------------------------------------------------------------*/
  415.             /* If this is the first numeral key after one or more non-        */
  416.             /* numerals, start a new string of numerals in the display        */
  417.             /*----------------------------------------------------------------*/
  418.  
  419.             m_nNumeral[0] = ndxCalcKey;
  420.             m_nNumerals   = 1;
  421.  
  422.             /*----------------------------------------------------------------*/
  423.             /* If the user presses a numeral immediately following the "="    */
  424.             /* key, then we need to clear the accumulator.                    */
  425.             /*----------------------------------------------------------------*/
  426.  
  427.             if ( m_ndxLastKey == m_ndxEquals )
  428.                {
  429.                m_nAccumulator    = 0;
  430.                m_ndxPrevOperator = m_ndxAdd;
  431.                }
  432.             }
  433.  
  434.          m_ndxLastKey = ndxCalcKey;
  435.          updateDisplay();
  436.          return;
  437.          }
  438.  
  439.  
  440.       /*----------------------------------------------------------------------*/
  441.       /*            Handle the keys for mathematical operations.              */
  442.       /*----------------------------------------------------------------------*/
  443.  
  444.       if ( ( ndxCalcKey == m_ndxAdd      ) ||
  445.            ( ndxCalcKey == m_ndxSubtract ) ||
  446.            ( ndxCalcKey == m_ndxMultiply ) ||
  447.            ( ndxCalcKey == m_ndxDivide   ) ||
  448.            ( ndxCalcKey == m_ndxEquals   ) )
  449.          {
  450.          /*-------------------------------------------------------------------*/
  451.          /* If the user types a +,-,*,/ directly after the "=" key, the       */
  452.          /* operator becomes the previous operator.                           */
  453.          /*-------------------------------------------------------------------*/
  454.  
  455.          if ( ( m_ndxLastKey == m_ndxEquals ) && ( ( ndxCalcKey == m_ndxAdd      ) || 
  456.                                                    ( ndxCalcKey == m_ndxSubtract ) || 
  457.                                                    ( ndxCalcKey == m_ndxMultiply ) || 
  458.                                                    ( ndxCalcKey == m_ndxDivide   ) ) )  
  459.             {
  460.             m_ndxLastKey      = ndxCalcKey;
  461.             m_ndxPrevOperator = ndxCalcKey;
  462.             return;
  463.             }
  464.  
  465.  
  466.          /*-------------------------------------------------------------------*/
  467.          /*            Ignore consecutive keypresses of +,-,*,/               */
  468.          /*-------------------------------------------------------------------*/
  469.  
  470.          if ( ( ndxCalcKey != m_ndxEquals ) && ( ( m_ndxLastKey == m_ndxAdd      ) || 
  471.                                                  ( m_ndxLastKey == m_ndxSubtract ) || 
  472.                                                  ( m_ndxLastKey == m_ndxMultiply ) || 
  473.                                                  ( m_ndxLastKey == m_ndxDivide   ) ) ) 
  474.             return;  
  475.              
  476.  
  477.          m_ndxLastKey = ndxCalcKey;
  478.  
  479.          if ( m_nNumerals > 0 )                   /*--------------------------*/
  480.             {                                     /* Ignore the keypress if   */
  481.             int nEntry = evaluateRomanNumeral();  /* the user has entered no  */
  482.                                                   /* numerals.                */
  483.             if ( nEntry < 0 )                     /*--------------------------*/
  484.                {
  485.                m_nAccumulator    = 0;             /*--------------------------*/
  486.                m_nNumerals       = 0;             /* Make sure the entry is   */
  487.                updateDisplay();                   /* a valid Roman Numeral    */
  488.                m_nError          = nEntry;        /*--------------------------*/
  489.                m_ndxPrevOperator = m_ndxAdd;
  490.                }
  491.             else
  492.                {
  493.                /*-------------------------------------------------------------*/
  494.                /* Perform the requested operation, and do a range check on    */
  495.                /* the result.                                                 */
  496.                /*-------------------------------------------------------------*/
  497.  
  498.                switch ( m_ndxPrevOperator )
  499.                   {
  500.                   case m_ndxAdd:      m_nAccumulator += nEntry; break;
  501.                   case m_ndxSubtract: m_nAccumulator -= nEntry; break;
  502.                   case m_ndxMultiply: m_nAccumulator *= nEntry; break;
  503.                   case m_ndxDivide:   m_nAccumulator /= nEntry;
  504.                   }
  505.  
  506.                if ( m_nAccumulator < m_nMinValue )
  507.                   {
  508.                   m_nAccumulator    = 0;      
  509.                   m_nNumerals       = 0; 
  510.                   updateDisplay();     
  511.                   m_nError          = m_nUNDERFLOW;
  512.                   m_ndxPrevOperator = m_ndxAdd; 
  513.                   return;
  514.                   }
  515.  
  516.                if ( m_nAccumulator > m_nMaxValue )
  517.                   {
  518.                   m_nAccumulator    = 0;      
  519.                   m_nNumerals       = 0; 
  520.                   updateDisplay();     
  521.                   m_nError          = m_nOVERFLOW; 
  522.                   m_ndxPrevOperator = m_ndxAdd;
  523.                   return;
  524.                   }
  525.  
  526.                if ( ndxCalcKey == m_ndxEquals )
  527.                   {
  528.                   convertToRomanNumerals ( m_nAccumulator );
  529.                   updateDisplay();
  530.                   }
  531.  
  532.                m_ndxPrevOperator = ndxCalcKey;
  533.                }
  534.             }
  535.  
  536.          return;
  537.          }
  538.  
  539.  
  540.       /*----------------------------------------------------------------------*/
  541.       /*                     Handle the "Clear Entry" key                     */
  542.       /*----------------------------------------------------------------------*/
  543.  
  544.       if ( ndxCalcKey == m_ndxClearEntry )
  545.          {
  546.          m_nNumerals  = 0;
  547.          m_ndxLastKey = ndxCalcKey;
  548.  
  549.          updateDisplay();
  550.  
  551.          return;
  552.          }
  553.  
  554.  
  555.       /*----------------------------------------------------------------------*/
  556.       /*                         Handle the "Clear" key                       */
  557.       /*----------------------------------------------------------------------*/
  558.  
  559.       if ( ndxCalcKey == m_ndxClear )
  560.          {
  561.          m_nNumerals       = 0;
  562.          m_nAccumulator    = 0;
  563.          m_ndxLastKey      = ndxCalcKey;
  564.          m_ndxPrevOperator = m_ndxAdd;
  565.  
  566.          updateDisplay();
  567.  
  568.          return;
  569.          }
  570.       }
  571.  
  572.  
  573.     /*-**************************** updateDisplay ****************************-*/
  574.    /*                                                                         */
  575.    /* Redraw the numeric display.  This function is called outside of the     */
  576.    /* context of a repaint.                                                   */
  577.    /*                                                                         */
  578.     /*-***********************************************************************-*/
  579.  
  580.    synchronized private void updateDisplay()
  581.       {
  582.       Graphics g = getGraphics();
  583.       drawDisplay ( g );
  584.       }
  585.  
  586.  
  587.     /*-***************************** drawDisplay *****************************-*/
  588.    /*                                                                         */
  589.    /*                    Draw the display of the calculator.                  */
  590.    /*                                                                         */
  591.     /*-***********************************************************************-*/
  592.    
  593.    synchronized private void drawDisplay ( Graphics gComponent )
  594.       {
  595.       /*----------------------------------------------------------------------*/
  596.       /*          Fill the display buffer with the background color           */
  597.       /*----------------------------------------------------------------------*/
  598.  
  599.       Graphics gBuffer = m_imageDisplay.getGraphics();
  600.       gBuffer.setColor ( m_colorDisplay );
  601.       gBuffer.fillRect ( 0, 0, m_nDisplayWidth, m_nDisplayHeight );
  602.  
  603.  
  604.       /*----------------------------------------------------------------------*/
  605.       /* Calculate the total width of the string of numerals to be displayed. */
  606.       /* Use this width to center the string in the display.                  */
  607.       /*----------------------------------------------------------------------*/
  608.  
  609.       int nWidth = 0;
  610.  
  611.       if ( m_nNumerals > 1 ) 
  612.          nWidth += ( m_nNumerals - 1 ) * m_nDisplayGap;
  613.  
  614.       for ( int i = 0; i < m_nNumerals; i++ )
  615.          nWidth += m_imageNumeral[m_nNumeral[i]].getWidth ( null );
  616.  
  617.  
  618.       /*----------------------------------------------------------------------*/
  619.       /*          Now draw the digits in the off-screen display buffer        */
  620.       /*----------------------------------------------------------------------*/
  621.  
  622.       int   nX = ( m_nDisplayWidth - nWidth ) / 2;
  623.       int   nY;
  624.       Image imageNumeral;
  625.  
  626.       for ( int i = 0; i < m_nNumerals ; i++ )
  627.          {
  628.          imageNumeral = m_imageNumeral[m_nNumeral[i]];
  629.          nY = m_nDisplayHeight - imageNumeral.getHeight ( null ) - m_nDisplayMargin;
  630.          gBuffer.drawImage ( imageNumeral, nX, nY, null );
  631.          nX += imageNumeral.getWidth ( null ) + m_nDisplayGap;
  632.          }
  633.  
  634.  
  635.       /*----------------------------------------------------------------------*/
  636.       /* The offscreen image buffer is now complete; draw it to the applet    */
  637.       /* window.                                                              */
  638.       /*----------------------------------------------------------------------*/
  639.  
  640.       gComponent.drawImage ( m_imageDisplay, m_nDisplayX0, m_nDisplayY0, null ); 
  641.       }
  642.  
  643.                                                                  
  644.     /*-************************ convertToRomanNumerals ***********************-*/
  645.    /*                                                                         */
  646.    /* Convert the argument value to a string of Roman Numerals, and place     */
  647.    /* them in the display array.                                              */
  648.    /*                                                                         */
  649.    /* Argument: int nValue   -- Value to be converted                         */
  650.    /*                                                                         */
  651.    /* Return:   int          -- m_nUNDERFLOW:  Value is less than 1           */
  652.    /* -------                   m_nOVERFLOW:   Value is greater than 3999999  */
  653.    /*                           0:             Value converted successfully.  */
  654.    /*                                                                         */
  655.     /*-***********************************************************************-*/
  656.  
  657.    synchronized private int convertToRomanNumerals ( int nValue )
  658.       {
  659.       m_nNumerals = 0;
  660.  
  661.       if ( nValue < m_nMinValue ) return m_nUNDERFLOW;
  662.       if ( nValue > m_nMaxValue ) return m_nOVERFLOW;
  663.  
  664.       /*----------------------------------------------------------------------*/
  665.       /* At this point, we know that the number can be represented in Roman   */
  666.       /* Numerals.  Go to it.                                                 */
  667.       /*----------------------------------------------------------------------*/
  668.  
  669.       int nResidual  = nValue;
  670.  
  671.       while ( nResidual >= 1000000 )
  672.          {
  673.          m_nNumeral[m_nNumerals] = m_ndxMbar;
  674.          m_nNumerals++;
  675.          nResidual -= 1000000;
  676.          }
  677.  
  678.  
  679.       if ( nResidual >= 900000 )
  680.          {
  681.          m_nNumeral[m_nNumerals] = m_ndxCbar;
  682.          m_nNumerals++;
  683.  
  684.          m_nNumeral[m_nNumerals] = m_ndxMbar;
  685.          m_nNumerals++;
  686.  
  687.          nResidual -= 900000;
  688.          }
  689.  
  690.  
  691.       if ( nResidual >= 500000 )
  692.          {
  693.          m_nNumeral[m_nNumerals] = m_ndxDbar;
  694.          m_nNumerals++;
  695.  
  696.          nResidual -= 500000;
  697.          }
  698.  
  699.  
  700.       if ( nResidual >= 400000 )
  701.          {
  702.          m_nNumeral[m_nNumerals] = m_ndxCbar;
  703.          m_nNumerals++;
  704.  
  705.          m_nNumeral[m_nNumerals] = m_ndxDbar;
  706.          m_nNumerals++;
  707.  
  708.          nResidual -= 400000;
  709.          }
  710.  
  711.  
  712.       while ( nResidual >= 100000 )
  713.          {
  714.          m_nNumeral[m_nNumerals] = m_ndxCbar;
  715.          m_nNumerals++;
  716.          nResidual -= 100000;
  717.          }
  718.  
  719.  
  720.       if ( nResidual >= 90000 )
  721.          {
  722.          m_nNumeral[m_nNumerals] = m_ndxXbar;
  723.          m_nNumerals++;
  724.  
  725.          m_nNumeral[m_nNumerals] = m_ndxCbar;
  726.          m_nNumerals++;
  727.  
  728.          nResidual -= 90000;
  729.          }
  730.  
  731.  
  732.       if ( nResidual >= 50000 )
  733.          {
  734.          m_nNumeral[m_nNumerals] = m_ndxLbar;
  735.          m_nNumerals++;
  736.  
  737.          nResidual -= 50000;
  738.          }
  739.  
  740.  
  741.       if ( nResidual >= 40000 )
  742.          {
  743.          m_nNumeral[m_nNumerals] = m_ndxXbar;
  744.          m_nNumerals++;
  745.  
  746.          m_nNumeral[m_nNumerals] = m_ndxLbar;
  747.          m_nNumerals++;
  748.  
  749.          nResidual -= 40000;
  750.          }
  751.  
  752.  
  753.       while ( nResidual >= 10000 )
  754.          {
  755.          m_nNumeral[m_nNumerals] = m_ndxXbar;
  756.          m_nNumerals++;
  757.          nResidual -= 10000;
  758.          }
  759.  
  760.  
  761.       if ( nResidual >= 9000 )
  762.          {
  763.          m_nNumeral[m_nNumerals] = m_ndxM;
  764.          m_nNumerals++;
  765.  
  766.          m_nNumeral[m_nNumerals] = m_ndxXbar;
  767.          m_nNumerals++;
  768.  
  769.          nResidual -= 9000;
  770.          }
  771.  
  772.  
  773.       if ( nResidual >= 5000 )
  774.          {
  775.          m_nNumeral[m_nNumerals] = m_ndxVbar;
  776.          m_nNumerals++;
  777.  
  778.          nResidual -= 5000;
  779.          }
  780.  
  781.  
  782.       if ( nResidual >= 4000 )
  783.          {
  784.          m_nNumeral[m_nNumerals] = m_ndxM;
  785.          m_nNumerals++;
  786.  
  787.          m_nNumeral[m_nNumerals] = m_ndxVbar;
  788.          m_nNumerals++;
  789.  
  790.          nResidual -= 4000;
  791.          }
  792.  
  793.  
  794.       while ( nResidual >= 1000 )
  795.          {
  796.          m_nNumeral[m_nNumerals] = m_ndxM;
  797.          m_nNumerals++;
  798.          nResidual -= 1000;
  799.          }
  800.  
  801.  
  802.       if ( nResidual >= 900 )
  803.          {
  804.          m_nNumeral[m_nNumerals] = m_ndxC;
  805.          m_nNumerals++;
  806.  
  807.          m_nNumeral[m_nNumerals] = m_ndxM;
  808.          m_nNumerals++;
  809.  
  810.          nResidual -= 900;
  811.          }
  812.  
  813.  
  814.       if ( nResidual >= 500 )
  815.          {
  816.          m_nNumeral[m_nNumerals] = m_ndxD;
  817.          m_nNumerals++;
  818.  
  819.          nResidual -= 500;
  820.          }
  821.  
  822.  
  823.       if ( nResidual >= 400 )
  824.          {
  825.          m_nNumeral[m_nNumerals] = m_ndxC;
  826.          m_nNumerals++;
  827.  
  828.          m_nNumeral[m_nNumerals] = m_ndxD;
  829.          m_nNumerals++;
  830.  
  831.          nResidual -= 400;
  832.          }
  833.  
  834.  
  835.       while ( nResidual >= 100 )
  836.          {
  837.          m_nNumeral[m_nNumerals] = m_ndxC;
  838.          m_nNumerals++;
  839.          nResidual -= 100;
  840.          }
  841.  
  842.  
  843.       if ( nResidual >= 90 )
  844.          {
  845.          m_nNumeral[m_nNumerals] = m_ndxX;
  846.          m_nNumerals++;
  847.  
  848.          m_nNumeral[m_nNumerals] = m_ndxC;
  849.          m_nNumerals++;
  850.  
  851.          nResidual -= 90;
  852.          }
  853.  
  854.  
  855.       if ( nResidual >= 50 )
  856.          {
  857.          m_nNumeral[m_nNumerals] = m_ndxL;
  858.          m_nNumerals++;
  859.  
  860.          nResidual -= 50;
  861.          }
  862.  
  863.  
  864.       if ( nResidual >= 40 )
  865.          {
  866.          m_nNumeral[m_nNumerals] = m_ndxX;
  867.          m_nNumerals++;
  868.  
  869.          m_nNumeral[m_nNumerals] = m_ndxL;
  870.          m_nNumerals++;
  871.  
  872.          nResidual -= 40;
  873.          }
  874.  
  875.  
  876.       while ( nResidual >= 10 )
  877.          {
  878.          m_nNumeral[m_nNumerals] = m_ndxX;
  879.          m_nNumerals++;
  880.          nResidual -= 10;
  881.          }
  882.  
  883.  
  884.       if ( nResidual >= 9 )
  885.          {
  886.          m_nNumeral[m_nNumerals] = m_ndxI;
  887.          m_nNumerals++;
  888.  
  889.          m_nNumeral[m_nNumerals] = m_ndxX;
  890.          m_nNumerals++;
  891.  
  892.          nResidual -= 9;
  893.          }
  894.    
  895.  
  896.       if ( nResidual >= 5 )
  897.          {
  898.          m_nNumeral[m_nNumerals] = m_ndxV;
  899.          m_nNumerals++;
  900.  
  901.          nResidual -= 5;
  902.          }
  903.  
  904.  
  905.       if ( nResidual >= 4 )
  906.          {
  907.          m_nNumeral[m_nNumerals] = m_ndxI;
  908.          m_nNumerals++;
  909.  
  910.          m_nNumeral[m_nNumerals] = m_ndxV;
  911.          m_nNumerals++;
  912.  
  913.          nResidual -= 4;
  914.          }
  915.  
  916.  
  917.       while ( nResidual >= 1 )
  918.          {
  919.          m_nNumeral[m_nNumerals] = m_ndxI;
  920.          m_nNumerals++;
  921.          nResidual -= 1;
  922.          }
  923.  
  924.  
  925.       return 0;
  926.       }
  927.  
  928.  
  929.     /*-************************ Evaluate Roman Numeral ***********************-*/
  930.    /*                                                                         */
  931.    /* Convert a Roman Numeral string to an integer.  This function returns    */
  932.    /* the value of the Roman Numeral or m_nINVALID.                           */
  933.    /*                                                                         */
  934.     /*-***********************************************************************-*/
  935.  
  936.    private int evaluateRomanNumeral ( )
  937.       {
  938.       int     nValue        =          0;
  939.       int     nRepeatCount  =          0;
  940.                                
  941.       int[]   nNumeralTable =  {       1,     /*-----------------------------*/
  942.                                        5,     /*                             */
  943.                                       10,     /*                             */
  944.                                       50,     /*                             */
  945.                                      100,     /*                             */
  946.                                      500,     /* This lookup table provides  */
  947.                                     1000,     /* the value of the numeral    */
  948.                                     5000,     /* associated with each index. */
  949.                                    10000,     /*                             */
  950.                                    50000,     /*                             */
  951.                                   100000,     /*                             */
  952.                                   500000,     /*                             */
  953.                                  1000000 };   /*-----------------------------*/
  954.  
  955.       /*----------------------------------------------------------------------*/
  956.       /*  Perform one iteration of this loop for each numeral in the string.  */
  957.       /*----------------------------------------------------------------------*/
  958.       
  959.       for ( int i = 0; i < m_nNumerals; i++ )
  960.          {
  961.          /*-------------------------------------------------------------------*/
  962.          /*              Handle the first numeral in the string.              */
  963.          /*-------------------------------------------------------------------*/
  964.  
  965.          if ( i == 0 )
  966.             { 
  967.             nValue = nNumeralTable[m_nNumeral[0]];
  968.             nRepeatCount = 1;
  969.             }
  970.  
  971.          else
  972.  
  973.          /*-------------------------------------------------------------------*/
  974.          /* The current numeral is identical to the numeral which immediately */
  975.          /* preceeds it.                                                      */
  976.          /*-------------------------------------------------------------------*/
  977.  
  978.          if ( m_nNumeral[i] == m_nNumeral[i-1] )     
  979.             {                                        /*-----------------------*/ 
  980.             if ( ( m_nNumeral[i] == m_ndxV    ) ||   /* The only numerals that*/ 
  981.                  ( m_nNumeral[i] == m_ndxL    ) ||   /* can repeat are powers */ 
  982.                  ( m_nNumeral[i] == m_ndxD    ) ||   /* of ten.               */ 
  983.                  ( m_nNumeral[i] == m_ndxVbar ) ||   /*-----------------------*/ 
  984.                  ( m_nNumeral[i] == m_ndxLbar ) ||
  985.                  ( m_nNumeral[i] == m_ndxDbar ) )  return m_nINVALID;
  986.  
  987.             else                                        /*--------------------*/
  988.                                                         /* No numeral can     */
  989.             if ( nRepeatCount >= 3 ) return m_nINVALID; /* repeat more than   */
  990.                                                         /* three times in a   */
  991.             else                                        /* row.               */
  992.                {                                        /*--------------------*/
  993.                nRepeatCount++;
  994.                nValue += nNumeralTable[m_nNumeral[i]];
  995.                }
  996.             }
  997.  
  998.          else
  999.  
  1000.          /*-------------------------------------------------------------------*/
  1001.          /* If the numeral is the smallest numeral so far, it is always valid */
  1002.          /*-------------------------------------------------------------------*/
  1003.  
  1004.          if ( ( i <= 1 ) && ( m_nNumeral[i] < m_nNumeral[i-1] ) )
  1005.             {
  1006.             nValue += nNumeralTable[m_nNumeral[i]];
  1007.             nRepeatCount = 1;
  1008.             }
  1009.  
  1010.          else
  1011.  
  1012.          if ( ( i > 1 ) && ( m_nNumeral[i] < m_nNumeral[i-1] ) && ( m_nNumeral[i] < m_nNumeral[i-2] ) )
  1013.             {
  1014.             nValue += nNumeralTable[m_nNumeral[i]];
  1015.             nRepeatCount = 1;
  1016.             }
  1017.  
  1018.          else
  1019.  
  1020.          /*-------------------------------------------------------------------*/
  1021.          /*                                                                   */
  1022.          /* A larger numeral can follow a smaller numeral only in certain     */
  1023.          /* cases:                                                            */
  1024.          /*                                                                   */
  1025.          /*    (1) The larger numeral must be no larger than the numeral      */
  1026.          /*        which precedes the smaller numeral.  (This implies that    */
  1027.          /*        the smaller numeral cannot be repeated.)                   */
  1028.          /*                                                                   */
  1029.          /*    (2) The smaller numeral must be a power of ten.                */
  1030.          /*                                                                   */
  1031.          /*    (3) The smaller numeral must be either one tenth or one fifth  */
  1032.          /*        of the value of the larger numeral                         */
  1033.          /*                                                                   */
  1034.          /*-------------------------------------------------------------------*/
  1035.  
  1036.          if ( m_nNumeral[i] > m_nNumeral[i-1] )        
  1037.             {
  1038.             if ( ( i > 1 ) &&                                /*---------------*/
  1039.                  ( m_nNumeral[i] > m_nNumeral[i-2] ) )       /*      (1)      */
  1040.                return m_nINVALID;                            /*---------------*/
  1041.                                                              
  1042.             else                                             
  1043.                                                              
  1044.             if ( ( m_nNumeral[i-1] == m_ndxV    ) ||         /*---------------*/
  1045.                  ( m_nNumeral[i-1] == m_ndxL    ) ||         /*      (2)      */
  1046.                  ( m_nNumeral[i-1] == m_ndxD    ) ||         /*---------------*/
  1047.                  ( m_nNumeral[i-1] == m_ndxVbar ) || 
  1048.                  ( m_nNumeral[i-1] == m_ndxLbar ) ||
  1049.                  ( m_nNumeral[i-1] == m_ndxDbar ) )  return m_nINVALID;
  1050.  
  1051.             else                       
  1052.                                                              /*---------------*/
  1053.             if ( m_nNumeral[i] > ( m_nNumeral[i-1] + 2 ) )   /*      (3)      */
  1054.                return m_nINVALID;                            /*---------------*/
  1055.                                                              
  1056.             else                       /*------------------------------------*/
  1057.                                        /* This is a valid case of a larger   */
  1058.                                        /* numeral following a smaller one.   */
  1059.                                        /* Set the repeat count to 3 to force */
  1060.                                        /* a smaller numeral to be next.      */
  1061.                {                       /*------------------------------------*/
  1062.                nRepeatCount = 3;
  1063.                nValue += nNumeralTable[m_nNumeral[i]];
  1064.                nValue -= 2 * nNumeralTable[m_nNumeral[i-1]];
  1065.                }
  1066.             }
  1067.  
  1068.  
  1069.          /*-------------------------------------------------------------------*/
  1070.          /* If the numeral falls into none of the above cases, it is invalid  */
  1071.          /*-------------------------------------------------------------------*/
  1072.  
  1073.          else return m_nINVALID;
  1074.          }
  1075.  
  1076.       return nValue;
  1077.       }
  1078.    
  1079.  
  1080.     /*-******************************** start ********************************-*/
  1081.    /*                                                                         */
  1082.    /*       This method is invoked every time the applet becomes active.      */
  1083.    /*                                                                         */
  1084.      /*-***********************************************************************-*/
  1085.   
  1086.    public void start()
  1087.       {
  1088.       if ( m_thread == null )
  1089.          {
  1090.          m_thread = new Thread ( this );
  1091.          m_thread.start();
  1092.          }
  1093.       }
  1094.  
  1095.  
  1096.     /*-******************************** stop *********************************-*/
  1097.    /*                                                                         */
  1098.    /*       This method is invoked every time the applet becomes inactive.    */
  1099.    /*                                                                         */
  1100.      /*-***********************************************************************-*/
  1101.   
  1102.    public void stop()
  1103.       {
  1104.       if ( m_thread != null )
  1105.          {
  1106.          m_thread.stop();
  1107.          m_thread = null;
  1108.          }
  1109.       }
  1110.  
  1111.  
  1112.     /*-********************************* run *********************************-*/
  1113.    /*                                                                         */
  1114.    /*      The background thread loads graphics and displays error codes.     */
  1115.    /*                                                                         */
  1116.     /*-***********************************************************************-*/
  1117.  
  1118.    public void run()
  1119.       {
  1120.       /*----------------------------------------------------------------------*/
  1121.       /* Load the applet's graphics the first time the background thread is   */
  1122.       /* run.                                                                 */
  1123.       /*----------------------------------------------------------------------*/
  1124.  
  1125.       if ( ! m_bInitialized )
  1126.          {
  1127.          m_imageNumeral[m_ndxI         ] = getImage ( getDocumentBase(), "i.gif"        );
  1128.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxI         ],   0 );
  1129.  
  1130.          m_imageNumeral[m_ndxV         ] = getImage ( getDocumentBase(), "v.gif"        );
  1131.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxV         ],   1 );
  1132.  
  1133.          m_imageNumeral[m_ndxX         ] = getImage ( getDocumentBase(), "x.gif"        );
  1134.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxX         ],   2 );
  1135.  
  1136.          m_imageNumeral[m_ndxL         ] = getImage ( getDocumentBase(), "l.gif"        );
  1137.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxL         ],   3 );
  1138.  
  1139.          m_imageNumeral[m_ndxC         ] = getImage ( getDocumentBase(), "c.gif"        );
  1140.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxC         ],   4 );
  1141.  
  1142.          m_imageNumeral[m_ndxD         ] = getImage ( getDocumentBase(), "d.gif"        );
  1143.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxD         ],   5 );
  1144.  
  1145.          m_imageNumeral[m_ndxM         ] = getImage ( getDocumentBase(), "m.gif"        );
  1146.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxM         ],   6 );
  1147.  
  1148.          m_imageNumeral[m_ndxVbar      ] = getImage ( getDocumentBase(), "vbar.gif"     );
  1149.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxVbar      ],   7 );
  1150.  
  1151.          m_imageNumeral[m_ndxXbar      ] = getImage ( getDocumentBase(), "xbar.gif"     );
  1152.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxXbar      ],   8 );
  1153.  
  1154.          m_imageNumeral[m_ndxLbar      ] = getImage ( getDocumentBase(), "lbar.gif"     );
  1155.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxLbar      ],   9 );
  1156.  
  1157.          m_imageNumeral[m_ndxCbar      ] = getImage ( getDocumentBase(), "cbar.gif"     );
  1158.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxCbar      ],  10 );
  1159.  
  1160.          m_imageNumeral[m_ndxDbar      ] = getImage ( getDocumentBase(), "dbar.gif"     );
  1161.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxDbar      ],  11 );
  1162.  
  1163.          m_imageNumeral[m_ndxMbar      ] = getImage ( getDocumentBase(), "mbar.gif"     );
  1164.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxMbar      ] ,  12 );
  1165.  
  1166.          m_imageNumeral[m_ndxAdd       ] = getImage ( getDocumentBase(), "add.gif"      );
  1167.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxAdd       ],  13 );
  1168.  
  1169.          m_imageNumeral[m_ndxSubtract  ] = getImage ( getDocumentBase(), "subtract.gif" );
  1170.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxSubtract  ],  14 );
  1171.  
  1172.          m_imageNumeral[m_ndxMultiply  ] = getImage ( getDocumentBase(), "multiply.gif" );
  1173.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxMultiply  ],  15 );
  1174.  
  1175.          m_imageNumeral[m_ndxDivide    ] = getImage ( getDocumentBase(), "divide.gif"   );
  1176.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxDivide    ],  16 );
  1177.  
  1178.          m_imageNumeral[m_ndxEquals    ] = getImage ( getDocumentBase(), "equals.gif"   );
  1179.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxEquals    ],  17 );
  1180.  
  1181.          m_imageNumeral[m_ndxClear     ] = getImage ( getDocumentBase(), "clearall.gif" );
  1182.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxClear     ],  18 );
  1183.  
  1184.          m_imageNumeral[m_ndxClearEntry] = getImage ( getDocumentBase(), "clear.gif"    );
  1185.             m_mediaTracker.addImage ( m_imageNumeral[m_ndxClearEntry],  19 );
  1186.  
  1187.          m_imageBackground = getImage ( getDocumentBase(), "calcbkgd.gif" );
  1188.             m_mediaTracker.addImage ( m_imageBackground,  20 );
  1189.  
  1190.          m_imageCaesar = getImage ( getDocumentBase(), "caesar.gif" );
  1191.             m_mediaTracker.addImage ( m_imageCaesar,  21 );
  1192.  
  1193.  
  1194.          /*-******************** Track graphics loading *********************-*/
  1195.          /*                                                                   */
  1196.          /* Track the progress of loading the applet graphics, and display it */
  1197.          /* as a bar graph                                                    */
  1198.          /*-*****************************************************************-*/
  1199.  
  1200.          Dimension dimWin         = size();
  1201.                    m_imageLoading = createImage ( dimWin.width, dimWin.height );
  1202.          Graphics  gBuffer        = m_imageLoading.getGraphics();
  1203.  
  1204.          int       nFrameWidth    = 200;
  1205.          int       nFrameHeight   = 30;
  1206.          int       nFrameThick    = 2;
  1207.                                  
  1208.          int       nFrameX        = ( dimWin.width - nFrameWidth ) / 2;
  1209.          int       nFrameY        = ( dimWin.height * 2 / 3 ) - ( nFrameHeight / 2 );
  1210.  
  1211.          int       nGraphX        = nFrameX + nFrameThick;
  1212.          int       nGraphY        = nFrameY + nFrameThick;
  1213.          int       nGraphWidth    = nFrameWidth - 2 * nFrameThick;
  1214.          int       nGraphHeight   = nFrameHeight - 2 * nFrameThick;
  1215.  
  1216.          int       nTotalImages   = 22;
  1217.          int       nGraphedImages = -1;
  1218.          int       nLoadedImages;
  1219.  
  1220.  
  1221.          gBuffer.setColor ( new Color ( 255, 255, 255 ) );
  1222.          gBuffer.fillRect ( 0, 0, dimWin.width, dimWin.height );
  1223.  
  1224.          /*-------------------------------------------------------------------*/
  1225.          /*            Initialize the bar graph by drawing the name           */
  1226.          /*-------------------------------------------------------------------*/
  1227.  
  1228.          gBuffer.setColor ( new Color (   0,   0,   0 ) );
  1229.          for ( int i = 0; i < nFrameThick; i++ )
  1230.             gBuffer.drawRect ( nFrameX + i, 
  1231.                                nFrameY + i, 
  1232.                                nFrameWidth - 2 * i - 1, 
  1233.                                nFrameHeight - 2 * i - 1 );
  1234.  
  1235.          gBuffer.setFont  ( new Font  ( "Helvetica", Font.PLAIN, 14 ) );
  1236.          FontMetrics fm = gBuffer.getFontMetrics();
  1237.  
  1238.          String stringLoading = "Loading graphics...";
  1239.          int    nStringX      = ( dimWin.width - fm.stringWidth ( stringLoading ) ) / 2;
  1240.          int    nStringY      = nFrameY - fm.getDescent() - 2;
  1241.          gBuffer.drawString ( stringLoading, nStringX, nStringY );
  1242.  
  1243.          /*-------------------------------------------------------------------*/
  1244.          /*                   Display the name of the applet                  */
  1245.          /*-------------------------------------------------------------------*/
  1246.  
  1247.          gBuffer.setFont  ( new Font  ( "Helvetica", Font.BOLD, 24 ) );
  1248.          fm = gBuffer.getFontMetrics();
  1249.  
  1250.          String stringApplet  = "COMPVTER ROMANVS";
  1251.          nStringX      = ( dimWin.width - fm.stringWidth ( stringApplet ) ) / 2;
  1252.          nStringY      = dimWin.height / 3;
  1253.          gBuffer.drawString ( stringApplet, nStringX, nStringY );
  1254.  
  1255.          nStringY += fm.getDescent();
  1256.          gBuffer.setColor ( Color.black );
  1257.          gBuffer.setFont  ( new Font  ( "Helvetica", Font.PLAIN, 14 ) );
  1258.          fm = gBuffer.getFontMetrics();
  1259.          nStringY += fm.getAscent() + fm.getLeading();
  1260.          String stringCopyright = "Copyright ⌐ MCMXCVII Edward R. Hobbs";
  1261.          nStringX  = ( dimWin.width - fm.stringWidth ( stringCopyright ) ) / 2;
  1262.          gBuffer.drawString ( stringCopyright, nStringX, nStringY );
  1263.  
  1264.          gBuffer.dispose();
  1265.  
  1266.          /*-------------------------------------------------------------------*/
  1267.          /* Loop ten times per second, checking to see how many graphics have */
  1268.          /* been loaded.  Redraw the graph every time progress is detected.   */
  1269.          /*-------------------------------------------------------------------*/
  1270.  
  1271.          for(;;)
  1272.             {
  1273.             nLoadedImages = 0;
  1274.  
  1275.             for ( int i = 0; i < nTotalImages; i++ )
  1276.                if ( m_mediaTracker.checkID ( i, true ) ) nLoadedImages++;
  1277.  
  1278.             if ( nLoadedImages > nGraphedImages )
  1279.                {
  1280.                nGraphedImages = nLoadedImages;
  1281.  
  1282.                gBuffer= m_imageLoading.getGraphics();
  1283.                gBuffer.setColor ( new Color (   0,   0, 255 ) );
  1284.  
  1285.                gBuffer.fillRect ( nGraphX, 
  1286.                                   nGraphY, 
  1287.                                 ( nGraphWidth * nGraphedImages ) / nTotalImages,
  1288.                                   nGraphHeight );
  1289.  
  1290.                gBuffer.dispose();
  1291.  
  1292.                Graphics gWindow = getGraphics();
  1293.                gWindow.drawImage ( m_imageLoading, 0, 0, dimWin.width, dimWin.height, null );
  1294.                gWindow.dispose();
  1295.                }
  1296.  
  1297.             if ( nGraphedImages >= nTotalImages ) break;
  1298.  
  1299.             try
  1300.                {
  1301.                Thread.sleep ( 100 );
  1302.                }
  1303.  
  1304.             catch ( InterruptedException e ) 
  1305.                {                             
  1306.                }                             
  1307.             }
  1308.  
  1309.          m_imageLoading.flush();
  1310.  
  1311.          /*-*****************************************************************-*/
  1312.          /*         At this point, all of the graphics have been loaded.      */
  1313.          /*-*****************************************************************-*/
  1314.  
  1315.          /*-------------------------------------------------------------------*/
  1316.          /*             Create the Calculator Keys for the Numerals           */
  1317.          /*-------------------------------------------------------------------*/
  1318.  
  1319.          int nNumPadX0 = 235;
  1320.          int nNumPadY0 =  65;
  1321.  
  1322.  
  1323.          m_calckey[m_ndxI]    = new CalcKey ( (int) ( nNumPadX0 + 1.5 * CalcKey.m_nWidth  ),
  1324.                                               nNumPadY0 + 3 * CalcKey.m_nHeight,
  1325.                                               m_imageNumeral[m_ndxI], 0 );
  1326.                                     
  1327.                                     
  1328.          m_calckey[m_ndxV]    = new CalcKey ( nNumPadX0,
  1329.                                               nNumPadY0 + 2 * CalcKey.m_nHeight,
  1330.                                               m_imageNumeral[m_ndxV], 0 );
  1331.                                     
  1332.          m_calckey[m_ndxX]    = new CalcKey ( nNumPadX0 + CalcKey.m_nWidth,
  1333.                                               nNumPadY0 + 2 * CalcKey.m_nHeight,
  1334.                                               m_imageNumeral[m_ndxX], 0 );
  1335.                                     
  1336.          m_calckey[m_ndxL]    = new CalcKey ( nNumPadX0 + 2 *CalcKey.m_nWidth,
  1337.                                               nNumPadY0 + 2 * CalcKey.m_nHeight,
  1338.                                               m_imageNumeral[m_ndxL], 0 );
  1339.                                     
  1340.          m_calckey[m_ndxC]    = new CalcKey ( nNumPadX0 + 3 * CalcKey.m_nWidth,
  1341.                                               nNumPadY0 + 2 * CalcKey.m_nHeight,
  1342.                                               m_imageNumeral[m_ndxC], 0 );
  1343.                                     
  1344.                                     
  1345.          m_calckey[m_ndxD]    = new CalcKey ( nNumPadX0,
  1346.                                               nNumPadY0 + CalcKey.m_nHeight,
  1347.                                               m_imageNumeral[m_ndxD], 0 );
  1348.                                     
  1349.          m_calckey[m_ndxM]    = new CalcKey ( nNumPadX0 + CalcKey.m_nWidth,
  1350.                                               nNumPadY0 + CalcKey.m_nHeight,
  1351.                                               m_imageNumeral[m_ndxM], 0 );
  1352.                                     
  1353.          m_calckey[m_ndxVbar] = new CalcKey ( nNumPadX0 + 2 * CalcKey.m_nWidth,
  1354.                                               nNumPadY0 + CalcKey.m_nHeight,
  1355.                                               m_imageNumeral[m_ndxVbar], 0 );
  1356.                                     
  1357.          m_calckey[m_ndxXbar] = new CalcKey ( nNumPadX0 + 3 * CalcKey.m_nWidth,
  1358.                                               nNumPadY0 + CalcKey.m_nHeight,
  1359.                                               m_imageNumeral[m_ndxXbar], 0 );
  1360.                                     
  1361.                                     
  1362.          m_calckey[m_ndxLbar] = new CalcKey ( nNumPadX0,
  1363.                                               nNumPadY0,
  1364.                                               m_imageNumeral[m_ndxLbar], 0 );
  1365.                                     
  1366.          m_calckey[m_ndxCbar] = new CalcKey ( nNumPadX0 + CalcKey.m_nWidth,
  1367.                                               nNumPadY0,
  1368.                                               m_imageNumeral[m_ndxCbar], 0 );
  1369.                                     
  1370.          m_calckey[m_ndxDbar] = new CalcKey ( nNumPadX0 + 2 * CalcKey.m_nWidth,
  1371.                                               nNumPadY0,
  1372.                                               m_imageNumeral[m_ndxDbar], 0 );
  1373.                                     
  1374.          m_calckey[m_ndxMbar] = new CalcKey ( nNumPadX0 + 3 * CalcKey.m_nWidth,
  1375.                                               nNumPadY0,
  1376.                                               m_imageNumeral[m_ndxMbar], 0 );
  1377.                                     
  1378.  
  1379.          /*-------------------------------------------------------------------*/
  1380.          /*         Create the Calculator Keys for arithmetic operators       */
  1381.          /*-------------------------------------------------------------------*/
  1382.  
  1383.          int nOperatorX0 = 395;
  1384.          int nOperatorY0 =  65;
  1385.  
  1386.  
  1387.          m_calckey[m_ndxAdd]      = new CalcKey ( nOperatorX0,
  1388.                                                   nOperatorY0,
  1389.                                                   m_imageNumeral[m_ndxAdd], 0 );
  1390.                                         
  1391.          m_calckey[m_ndxSubtract] = new CalcKey ( nOperatorX0,
  1392.                                                   nOperatorY0 + CalcKey.m_nHeight,
  1393.                                                   m_imageNumeral[m_ndxSubtract], 0 );
  1394.                                         
  1395.          m_calckey[m_ndxMultiply] = new CalcKey ( nOperatorX0,
  1396.                                                   nOperatorY0 + 2 * CalcKey.m_nHeight,
  1397.                                                   m_imageNumeral[m_ndxMultiply], 0 );
  1398.                                         
  1399.          m_calckey[m_ndxDivide]   = new CalcKey ( nOperatorX0,
  1400.                                                   nOperatorY0 + 3 * CalcKey.m_nHeight,
  1401.                                                   m_imageNumeral[m_ndxDivide], 0 );
  1402.  
  1403.                                     
  1404.  
  1405.          /*-------------------------------------------------------------------*/
  1406.          /*             Create the special purpose Calculator Keys            */
  1407.          /*-------------------------------------------------------------------*/
  1408.  
  1409.          int nSpecialX0 = 450;
  1410.          int nSpecialY0 =  65;
  1411.  
  1412.  
  1413.          m_calckey[m_ndxClear]      = new CalcKey ( nSpecialX0,
  1414.                                                     nSpecialY0,
  1415.                                                     m_imageNumeral[m_ndxClear], 0 );
  1416.                                         
  1417.          m_calckey[m_ndxClearEntry] = new CalcKey ( nSpecialX0,
  1418.                                                     nSpecialY0 + CalcKey.m_nHeight,
  1419.                                                     m_imageNumeral[m_ndxClearEntry], 0 );
  1420.                                         
  1421.          m_calckey[m_ndxEquals]     = new CalcKey ( nSpecialX0,
  1422.                                                     nSpecialY0 + 3 * CalcKey.m_nHeight,
  1423.                                                     m_imageNumeral[m_ndxEquals], 0 );
  1424.  
  1425.  
  1426.          /*-------------------------------------------------------------------*/
  1427.          /*       Create the offscreen buffer for the calculator display.     */
  1428.          /*-------------------------------------------------------------------*/
  1429.  
  1430.          m_imageDisplay = createImage ( m_nDisplayWidth, m_nDisplayHeight );
  1431.          m_bInitialized = true;
  1432.  
  1433.          repaint();
  1434.          }
  1435.  
  1436.  
  1437.       /*------------------------- Display Error Code -------------------------*/
  1438.       /*                                                                      */
  1439.       /* The background thread spends most of its time in the following loop. */
  1440.       /* It wakes up at intervals to check for errors.  If an error is        */
  1441.       /* indicated, it blinks the error code in the display.                  */
  1442.       /*----------------------------------------------------------------------*/
  1443.  
  1444.       for(;;)
  1445.          {
  1446.          m_bErrorPhase = ! m_bErrorPhase;
  1447.  
  1448.          if ( m_nError < 0 )
  1449.             {
  1450.             if ( m_bErrorPhase )
  1451.                convertToRomanNumerals ( -m_nError );
  1452.             else
  1453.                m_nNumerals = 0;
  1454.  
  1455.             updateDisplay();
  1456.             }
  1457.  
  1458.          try
  1459.             {
  1460.             m_thread.sleep ( 500 );
  1461.             }
  1462.  
  1463.          catch ( InterruptedException e )
  1464.             {
  1465.             stop();
  1466.             }
  1467.          }
  1468.       }
  1469.    }
  1470.  
  1471.  
  1472. /*-*******************************             ******************************-*/
  1473. /*-*******************************   CalcKey   ******************************-*/
  1474. /*-*                                                                        *-*/
  1475. /*-*         This class defines the behavior of the calculator keys         *-*/
  1476. /*-*                                                                        *-*/
  1477. /*-**************************************************************************-*/
  1478. /*-**************************************************************************-*/
  1479.  
  1480. class CalcKey
  1481.    {
  1482.    /*-------------------------------------------------------------------------*/
  1483.    /*                   Constant data members of this class                   */
  1484.    /*-------------------------------------------------------------------------*/
  1485.  
  1486.    public final static int m_nWidth  = 35;
  1487.    public final static int m_nHeight = 35;
  1488.  
  1489.    final int    m_nFrameWidth = 2;
  1490.  
  1491.    final Color  m_colorBackground = new Color ( 192, 192, 192 );
  1492.    final Color  m_colorFrameLight = new Color ( 255, 255, 255 );
  1493.    final Color  m_colorFrameDark  = new Color (  80,  80,  80 );
  1494.  
  1495.  
  1496.    /*-------------------------------------------------------------------------*/
  1497.    /*                   Variable data members of this class                   */
  1498.    /*-------------------------------------------------------------------------*/
  1499.  
  1500.    int        m_nX;
  1501.    int        m_nY;
  1502.              
  1503.    Image      m_image;
  1504.    boolean    m_bPressed = false;
  1505.    public int m_nValue;
  1506.  
  1507.  
  1508.    /*-***********************************************************************-*/
  1509.    /*                           Class Constructor                             */
  1510.    /*-***********************************************************************-*/
  1511.  
  1512.    public CalcKey ( int nX, int nY, Image image, int nValue )
  1513.       {
  1514.       m_nX     = nX;
  1515.       m_nY     = nY;
  1516.       m_image  = image;
  1517.       m_nValue = nValue;
  1518.       }
  1519.  
  1520.  
  1521.    /*-****************************** testHit ********************************-*/
  1522.    /*                                                                         */
  1523.    /* Return true if the coordinates lie within this calculator key; return   */
  1524.    /* false if they do not.                                                   */
  1525.    /*                                                                         */
  1526.    /*-***********************************************************************-*/
  1527.  
  1528.    public boolean testHit ( int nX, int nY )
  1529.       {
  1530.       if ( ( nX >= m_nX ) && ( nX < ( m_nX + m_nWidth ) ) &&
  1531.            ( nY >= m_nY ) && ( nY < ( m_nY + m_nHeight) ) )
  1532.          return true;
  1533.  
  1534.       return false;
  1535.       }
  1536.  
  1537.  
  1538.    /*-***************************** setPressed ******************************-*/
  1539.    /*                                                                         */
  1540.    /* Compare the current state of the calculator key to the argument state.  */
  1541.    /* If the two states disagree, update the state of the calculator key, and */
  1542.    /* redraw it.                                                              */
  1543.    /*                                                                         */
  1544.    /*-***********************************************************************-*/
  1545.  
  1546.    public boolean setPressed ( boolean bPressed, Component component )
  1547.       {
  1548.       if ( bPressed != m_bPressed )
  1549.          {
  1550.          m_bPressed = bPressed;
  1551.          Draw ( component );
  1552.          return m_bPressed;
  1553.          }
  1554.  
  1555.       return false;
  1556.       }
  1557.  
  1558.  
  1559.    /*-******************************** Draw *********************************-*/
  1560.    /*                                                                         */
  1561.    /*         Draw a calculator key into the given Component object           */
  1562.    /*                                                                         */
  1563.    /*-***********************************************************************-*/
  1564.  
  1565.    public void Draw ( Component component )
  1566.       {
  1567.       Graphics g = component.getGraphics();
  1568.       Paint ( g, component );
  1569.       }
  1570.  
  1571.  
  1572.    /*-******************************** Paint ********************************-*/
  1573.    /*                                                                         */
  1574.    /*           Paint a calculator key into the given Graphics object         */
  1575.    /*                                                                         */
  1576.    /*-***********************************************************************-*/
  1577.  
  1578.    public void Paint ( Graphics g, Component component )
  1579.       {
  1580.       /*----------------------------------------------------------------------*/
  1581.       /*       Draw the top and left sides of the calculator key frame.       */
  1582.       /*----------------------------------------------------------------------*/
  1583.  
  1584.       if ( m_bPressed )
  1585.          g.setColor ( m_colorFrameDark );
  1586.       else
  1587.          g.setColor ( m_colorFrameLight );
  1588.  
  1589.       for ( int i = 0; i < m_nFrameWidth; i++ )
  1590.          {
  1591.          g.drawLine ( m_nX + i, m_nY + i, m_nX + m_nWidth - 1 - i, m_nY + i );
  1592.          g.drawLine ( m_nX + i, m_nY + i, m_nX + i, m_nY + m_nHeight - 1 - i );
  1593.          }
  1594.  
  1595.  
  1596.       /*----------------------------------------------------------------------*/
  1597.       /*     Draw the bottom and right sides of the calculator key frame.     */
  1598.       /*----------------------------------------------------------------------*/
  1599.  
  1600.       if ( m_bPressed )
  1601.          g.setColor ( m_colorFrameLight );
  1602.       else
  1603.          g.setColor ( m_colorFrameDark  );
  1604.  
  1605.       for ( int i = 0; i < m_nFrameWidth; i++ )
  1606.          {
  1607.          g.drawLine ( m_nX + m_nWidth - 1 - i, m_nY + m_nHeight - 1 - i,
  1608.                       m_nX + i, m_nY + m_nHeight - 1 - i );
  1609.  
  1610.          g.drawLine ( m_nX + m_nWidth - 1 - i, m_nY + m_nHeight - 1 - i,
  1611.                       m_nX + m_nWidth - 1 - i, m_nY + i );
  1612.          }
  1613.  
  1614.  
  1615.       /*----------------------------------------------------------------------*/
  1616.       /*     Draw the surface of the calculator key within the frame          */
  1617.       /*----------------------------------------------------------------------*/
  1618.  
  1619.       g.setColor ( m_colorBackground );
  1620.  
  1621.       g.fillRect ( m_nX + m_nFrameWidth, m_nY + m_nFrameWidth, 
  1622.                    m_nWidth - 2 * m_nFrameWidth, m_nHeight - 2 * m_nFrameWidth );
  1623.  
  1624.       int nX = m_nX + ( m_nWidth  - m_image.getWidth ( component ) ) / 2;
  1625.       int nY = m_nY + ( m_nHeight - m_image.getHeight ( component ) ) / 2;
  1626.  
  1627.       if ( m_bPressed )
  1628.          {
  1629.          nX++;
  1630.          nY++;
  1631.          }
  1632.  
  1633.       g.drawImage ( m_image, nX, nY, null );
  1634.       }
  1635.    }
  1636.