home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / files / acc / calcs / beancalc.c < prev    next >
C/C++ Source or Header  |  1989-02-22  |  16KB  |  593 lines

  1. #include <aesbind.h>
  2. #include <obdefs.h>
  3. #include <gemdefs.h>
  4. #include <osbind.h>
  5. #include <xbios.h>
  6. #include <math.h>
  7.  
  8. /* RESOURCE SET INDICES FOR CALC */
  9.  
  10. #define CALC        0    /* form / dialog */
  11. #define DISPLAY        1    /* BUTTON in tree CALC */
  12. #define NEGATE        2    /* BUTTON in tree CALC */
  13. #define CLEARD        3    /* BUTTON in tree CALC */
  14. #define CLEAR        4    /* BUTTON in tree CALC */
  15. #define CBOX        5    /* IBOX   in tree CALC */
  16. #define LPAREN        6    /* BUTTON in tree CALC */
  17. #define RPAREN        7    /* BUTTON in tree CALC */
  18. #define DIV        8    /* BUTTON in tree CALC */
  19. #define MULT        9    /* BUTTON in tree CALC */
  20. #define B7        10    /* BUTTON in tree CALC */
  21. #define B8        11    /* BUTTON in tree CALC */
  22. #define B9        12    /* BUTTON in tree CALC */
  23. #define MINUS        13    /* BUTTON in tree CALC */
  24. #define B4        14    /* BUTTON in tree CALC */
  25. #define B5        15    /* BUTTON in tree CALC */
  26. #define B6        16    /* BUTTON in tree CALC */
  27. #define PLUS        17    /* BUTTON in tree CALC */
  28. #define B1        18    /* BUTTON in tree CALC */
  29. #define B2        19    /* BUTTON in tree CALC */
  30. #define B3        20    /* BUTTON in tree CALC */
  31. #define EQUALS        21    /* BUTTON in tree CALC */
  32. #define B0        22    /* BUTTON in tree CALC */
  33. #define DECIMAL        23    /* BUTTON in tree CALC */        
  34.  
  35. /* display control constants */
  36.  
  37. #define    MAX_DISP    9        /* display width, chars */
  38. #define MAX_DIGS    MAX_DISP - 1    /* above, minus sign char */
  39. #define NXTOLAST    MAX_DISP - 2    /* position left of decimal pt. */
  40. #define VERYLAST    MAX_DISP - 1    /* last char in display */
  41. #define SIGN        0        /* sign char offset */
  42. #define FSTPOS        1        /* first digit position */
  43. #define SNDPOS        2        /* second digit position */
  44.  
  45. /* ST numeric keypad scancodes */
  46.  
  47. #define K_CLHM        0x47        /* clear/ home key */
  48. #define K_MINUS        0x4a        /* minus key */
  49. #define K_PLUS        0x4e        /* plus key */
  50. #define K_UNDO        0x61        /* UNDO key */
  51. #define K_HELP        0x62        /* HELP key */
  52. #define K_LP        0x63        /* left paren key */
  53. #define K_EQ        0x72        /* enter key */
  54.  
  55. /* calculator machine states */
  56.  
  57. #define MNEWENTRY    0        /* start of operand input */
  58. #define MENTRY        1        /* operand of input in progress */
  59. #define MOPERATOR    2        /* operator key pressed */
  60. #define MEQUALS        3        /* equals key pressed */
  61.  
  62. /* various and sundry defines */
  63.  
  64. #define NOWINDOW    -1            /* no window open */
  65. #define WTYPE        (NAME | CLOSER | MOVER)    /* window attributes */
  66. #define HI_RES        2            /* monochrome monitor */
  67. #define NUM_OBJS    24            /* objects in resource */
  68. #define NO_OBJ        -1            /* "no object" code */
  69. #define MAX_PLEVELS    49            /* paren nesting limit */
  70.  
  71. /* Inline functions: push and pop operands, operators, flags, etc. */
  72.  
  73. #define push_opd(op)    (opd_stack[opdsp++] = (op))
  74. #define pop_opd()    (opd_stack[--opdsp])
  75.  
  76. #define push_opr(op)    (opr_stack[oprsp++] = (op))
  77. #define pop_opr()    (opr_stack[--oprsp])
  78.  
  79. #define push_opf(op)    (opf_stack[opfsp++] = (op))
  80. #define pop_opf()    (opf_stack[--opfsp])
  81.  
  82. #define rdisp()        objc_draw(calc, DISPLAY, MAX_DEPTH, CRECT)
  83. #define display        ((char *) calc[DISPLAY].ob_spec)
  84.  
  85. /* convenience equates to shorten window-management expressions */
  86.  
  87. #define CX        calc[0].ob_x
  88. #define CY        calc[0].ob_y
  89. #define CW        calc[0].ob_width
  90. #define CH        calc[0].ob_height
  91. #define CRECT        CX, CY, CW, CH
  92. #define CPRECT        &CX, &CY, &CW, &CH
  93. #define WRECT        w.x, w.y, w.w, w.h
  94. #define WPRECT        &w.x, &w.y, &w.w, &w.h
  95. #define TRECT        t.x, t.y, t.w, t.h
  96. #define TPRECT        &t.x, &t.y, &t.w, &t.h
  97.  
  98. /* globals */
  99.  
  100. /* calculator resource data structure -- generated from .RSC file */
  101.             
  102. OBJECT calc[] =     {
  103.  
  104. 0xFFFF,   0x1,    0x5, 0x14,  0x0, 0x0,     0x1173L, 0x15F,  0x9, 0x63, 0x53,
  105. 0x2,   0xFFFF, 0xFFFF, 0x1A,  0x0, 0x0, "       0.",   0x9,  0x2, 0x50,  0x8,
  106. 0x3,   0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         "N",   0xA,  0xD, 0x10,  0x8,
  107. 0x4,   0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         "D",  0x35,  0xD,  0xF,  0x8,
  108. 0x5,   0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         "C",  0x49,  0xD, 0x10,  0x8,
  109. 0x0,      0x6,   0x17, 0x14,  0x0, 0x0,    0x11142L,   0x5, 0x17, 0x59, 0x3A,
  110. 0x7,   0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         "(",   0x5,  0x3, 0x10,  0x8,
  111. 0x8,   0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         ")",  0x1A,  0x3, 0x10,  0x8,
  112. 0x9,   0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         "/",  0x2F,  0x3, 0x10,  0x8,
  113. 0xA,   0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         "*",  0x44,  0x3, 0x10,  0x8,
  114. 0xB,   0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         "7",   0x5,  0xE, 0x10,  0x8,
  115. 0xC,   0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         "8",  0x1A,  0xE, 0x10,  0x8,
  116. 0xD,   0xFFFF, 0xFFFF, 0x1A,0x841, 0x0,         "9",  0x2F,  0xE, 0x10,  0x8,
  117. 0xE,   0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         "-",  0x44,  0xE, 0x10,  0x8,
  118. 0xF,   0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         "4",   0x5, 0x19, 0x10,  0x8,
  119. 0x10,  0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         "5",  0x1A, 0x19, 0x10,  0x8,
  120. 0x11,  0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         "6",  0x2F, 0x19, 0x10,  0x8,
  121. 0x12,  0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         "+",  0x44, 0x19, 0x10,  0x8,
  122. 0x13,  0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         "1",   0x5, 0x24, 0x10,  0x8,
  123. 0x14,  0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         "2",  0x1A, 0x24, 0x10,  0x8,
  124. 0x15,  0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         "3",  0x2F, 0x24, 0x10,  0x8,
  125. 0x16,  0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         "=",  0x44, 0x24, 0x10, 0x13,
  126. 0x17,  0xFFFF, 0xFFFF, 0x1A, 0x41, 0x0,         "0",   0x5, 0x2F, 0x25,  0x8,
  127. 0x5,   0xFFFF, 0xFFFF, 0x1A, 0x61, 0x0,         ".",  0x2F, 0x2F, 0x10,  0x8
  128.  
  129.             };
  130.  
  131.  
  132. char *noblanks();        /* blank_stripping function */
  133. char opr_stack[50];        /* operator stack */
  134. double opd_stack[50];        /* operand stack */
  135. char opf_stack[50];        /* flag stack */
  136. int oprsp, opdsp, opfsp;    /* stack pointers */
  137. int levels;            /* parenthesis levels */
  138. int op = MNEWENTRY;        /* calculator state */
  139.  
  140.  
  141. /******************************************************************************
  142.             MAIN PROGRAM
  143. ******************************************************************************/
  144.  
  145. main()
  146. {
  147.  
  148.     extern int gl_apid;
  149.     
  150.     appl_init();
  151.     menu_register(gl_apid, "  Calculator");
  152.     events();
  153.  
  154. }
  155.  
  156.  
  157. /******************************************************************************
  158.             ACCESSORY EVENT MANAGER
  159. *******************************************************************************/
  160.  
  161. events()
  162. {
  163.  
  164.     int ret, mx, my, key, dummy, message[8];
  165.     Mouse mouse = { &mx, &my, &dummy, &dummy };
  166.     Rect w, t;                /* window & temp rects */
  167.     int win = NOWINDOW;            /* window handle */
  168.     int obj;                /* object number */
  169.     int xd, yd;                /* window offsets */
  170.     int i;                    /* looping variable */
  171.     
  172.     if (Getrez() == HI_RES)
  173.         for (i = 0; i < NUM_OBJS; i++)    {
  174.             calc[i].ob_height = calc[i].ob_height * 2;
  175.             calc[i].ob_y = calc[i].ob_y * 2;
  176.                         }
  177.  
  178.     
  179.     form_center(calc, CPRECT);        /* center calculator */
  180.     wind_calc(0, WTYPE, CRECT, WPRECT);    /* get window coords */
  181.     xd = CX - w.x; yd = CY - w.y;        /* set differences   */
  182.                 
  183.     while (1)    {            /* endless loop */
  184.  
  185.         ret = evnt_multi(MU_KEYBD | MU_BUTTON | MU_MESAG, 1, 1, 1,
  186.             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, message, 0, 0, mouse,
  187.             &key, &dummy);
  188.         
  189.         if (ret & MU_MESAG)
  190.                          
  191.             switch(message[0])    {
  192.                 
  193.                 case (AC_OPEN):
  194.                     if (win == NOWINDOW)    {
  195.                     win = wind_create(WTYPE, WRECT);
  196.                     wind_set(win, WF_NAME, "BeanCalc",0,0);
  197.                     wind_open(win, WRECT);
  198.                                 }
  199.                     else
  200.                     wind_set(win, WF_TOP, 0, 0, 0, 0);
  201.                     break;
  202.  
  203. /****** if the system is about to take the window away, give it up *******/
  204.  
  205.                 case (AC_CLOSE):
  206.                     win = NOWINDOW;
  207.                     break;
  208.  
  209. /****** if someone wants the window shut down, close and delete it *******/
  210.  
  211.                 case (WM_CLOSED):
  212.                     wind_close(win);    
  213.                     wind_delete(win);
  214.                     win = NOWINDOW;
  215.                     break;
  216. /****** if someone moved it, update coordinates and reset it ************/
  217.  
  218.                 case (WM_MOVED):
  219.                     w.x = message[4];
  220.                     w.y = message[5];
  221.                     CX = w.x + xd;
  222.                     CY = w.y + yd;
  223.                     wind_set(win, WF_CURRXYWH, WRECT);
  224.                     break;
  225.  
  226. /****** if system wants it on top, top it ********************************/
  227.  
  228.                 case (WM_TOPPED):
  229.                 case (WM_NEWTOP):
  230.                     wind_set(win, WF_TOP, 0, 0, 0, 0);
  231.                     break;
  232.  
  233. /****** if system wants it redrawn, chase rectangle list to redraw it ****/
  234.  
  235.                 case (WM_REDRAW):
  236.                     wind_update(BEG_UPDATE);
  237.                     graf_mouse(M_OFF, 0L);
  238.                     wind_get(win, WF_FIRSTXYWH, TPRECT);
  239.                     while (t.w && t.h)    {
  240.                     objc_draw(calc, 0, MAX_DEPTH, TRECT);
  241.                     wind_get(win, WF_NEXTXYWH, TPRECT);
  242.                                 }
  243.                     graf_mouse(M_ON, 0L);
  244.                     wind_update(END_UPDATE);
  245.  
  246.  
  247.                     } /* end of switch(message[0]) */
  248.  
  249. /****** if we get a keyboard message, convert the key to an appropriate
  250. ASCII value and send it to the calculator manager **********************/
  251.  
  252.  
  253.         if (ret & MU_KEYBD)
  254.             if (obj = keyconv(key >> 8))
  255.                 manage_calc(obj);
  256.  
  257. /****** if we get a mouse button message, determine which object the user
  258. clicked, and manage the calculator **************************************/
  259.  
  260.         if (ret & MU_BUTTON)    {
  261.             
  262.             obj = objc_find(calc, CALC, MAX_DEPTH, mx, my);
  263.             if (obj != NO_OBJ && obj != CALC && obj != CBOX
  264.                 && obj != DISPLAY)
  265.                 manage_calc(obj);
  266.  
  267.                     }
  268.  
  269.  
  270.             }    /* end of while(1) */
  271.  
  272. }    /*************** end of events() ***********************/
  273.  
  274.  
  275. /******************************************************************************
  276.         CONVERT KEYBODE TO OBJECT NUMBER
  277.  
  278.     Most of this can be handled by using the scancode as an index into an
  279. array.  Otherwise handle special cases via switch.
  280. ******************************************************************************/
  281.  
  282.  
  283. keyconv(key)
  284. unsigned int key;
  285. {
  286.  
  287.     static int keypad[] = {
  288.         LPAREN, RPAREN, DIV, MULT, 
  289.         B7, B8, B9,
  290.         B4, B5, B6,
  291.         B1, B2, B3,
  292.         B0, DECIMAL, EQUALS };
  293.  
  294.     if (key >= K_LP && key <= K_EQ)
  295.         return(keypad[key - K_LP]);
  296.  
  297.     switch(key)    {
  298.         case K_HELP:    return(NEGATE);
  299.         case K_UNDO:    return(CLEAR);
  300.         case K_CLHM:    return(CLEARD);
  301.         case K_MINUS:    return(MINUS);
  302.         case K_PLUS:    return(PLUS);
  303.             }
  304.  
  305.     return(0);
  306.  
  307. }
  308.  
  309.  
  310. /******************************************************************************
  311.             MANAGE CALCULATOR
  312. ******************************************************************************/
  313.  
  314. manage_calc(obj)
  315. int obj;
  316. {
  317.  
  318.     static opf = 0;                /* operand flag */
  319.     
  320.     objc_change(calc, obj, 0, CRECT, SELECTED, 1);    /* select object */
  321.                 
  322.     switch(obj)    {
  323.  
  324.     /* if the clear key has been pressed, zero all stack pointers and
  325.     the level count, clear the display, and set for new entry */
  326.  
  327.         case (CLEAR):
  328.             opf = opdsp = oprsp = levels = 0;
  329.             clear_disp();
  330.             op = MNEWENTRY;
  331.             break;
  332.  
  333.     /* if the clear display key has been pressed and the machine is in
  334.     the process of operand entry, clear the display and set as if operand
  335.     entry were beginning */
  336.  
  337.         case (CLEARD):
  338.             if (op == MENTRY)    {
  339.                 clear_disp();
  340.                 op = MNEWENTRY;
  341.                         }
  342.                 break;
  343.  
  344.     /* if an operator key has been pressed and the machine is awaiting
  345.     operator entry, process the operator.  Start by pusing the operand
  346.     on the display, and switch the operand count flag.  Call evaluate()
  347.     if the operand count is > 0.  Then push the operator. */
  348.  
  349.         case (PLUS):
  350.         case (MINUS):
  351.         case (MULT):
  352.         case (DIV):
  353.             if (op == MENTRY || op == MEQUALS)    {
  354.                 push_opd(atof(noblanks()));
  355.                 opf++;
  356.                 if (opf == 2)    {
  357.                     evaluate();
  358.                     opf = 1;
  359.                         }
  360.                 push_opr(obj);
  361.                 op = MNEWENTRY;
  362.                                 }
  363.             break;
  364.  
  365.     /* If the equals key has been pressed and the machine is capable of
  366.     performing a pending evaluation, push the operand on the display
  367.     and do it. */
  368.  
  369.         case (EQUALS):
  370.             if (op == MENTRY && opf == 1)    {
  371.                 push_opd(atof(noblanks()));
  372.                 evaluate();
  373.                 pop_opd();
  374.                 opf = 0;
  375.                 op = MEQUALS;
  376.                             }
  377.             break;
  378.  
  379.     /* If the negate key has been pressed invert the sign of the display */
  380.  
  381.         case (NEGATE):
  382.             display[SIGN] = (display[SIGN] == '-') ? ' ' : '-';
  383.             rdisp();
  384.             break;
  385.  
  386.     /* If the left paren key has been pressed and the machine is awaiting
  387.     operand entry, bump up a paren level */
  388.  
  389.         case (LPAREN):
  390.             if (op == MNEWENTRY && levels < MAX_PLEVELS)    {
  391.                 push_opf(opf);
  392.                 opdsp++;
  393.                 oprsp++;
  394.                 levels++;
  395.                 opf = 0;
  396.                                     }
  397.             break;
  398.  
  399.     /* If the right paren key has been pressed and the machine is
  400.     capable of performing a pending evaluation, do it and jump down
  401.     a paren level */
  402.  
  403.         case (RPAREN):
  404.             if (op == MENTRY && opf && levels)    {
  405.                 push_opd(atof(noblanks()));
  406.                 evaluate();
  407.                 pop_opd();
  408.                 levels--;
  409.                 opdsp--;
  410.                 oprsp--;
  411.                 opf = pop_opf();
  412.                                 }
  413.             break;
  414.  
  415.     /* Otherwise it must be a number key or something similar, so pass
  416.     it on to the operand entry manager */
  417.  
  418.         default:
  419.             do_entry(obj);
  420.  
  421.             }    /* end of switch(obj) **********************/
  422.  
  423.     objc_change(calc, obj, 0, CRECT, NORMAL, 1);    /* deselect object */
  424.  
  425.  
  426. }    /************** end of manage_calc(obj) *****************************/
  427.  
  428.  
  429. /******************************************************************************
  430.             CLEAR PHYSICAL DISPLAY
  431. ******************************************************************************/
  432.  
  433. clear_disp()
  434. {
  435.  
  436.     strcpy(display, "       0.");
  437.     rdisp();
  438.  
  439. }
  440.  
  441.  
  442. /******************************************************************************
  443.     MAKE ENTRY:  ENTER DIGIT OR DECIMAL, CHANGE SIGN
  444. *******************************************************************************/
  445.  
  446. do_entry(v)
  447. int v;
  448. {
  449.  
  450.     static int dp = 0, nd = 0;
  451.  
  452.     if (op == MNEWENTRY)    {
  453.         clear_disp();
  454.         dp = nd = 0;
  455.         if (v == B0)
  456.             return;
  457.                 }
  458.  
  459.     if (v == DECIMAL)    {
  460.         dp = 1;
  461.         if (op == MNEWENTRY)
  462.             nd = 1;
  463.                 }
  464.  
  465.     else    {
  466.         if (op == MNEWENTRY || (display[SIGN] == '-' && nd == 0))
  467.             display[NXTOLAST] = ' ';
  468.         if ((nd + 1) < MAX_DIGS)    {
  469.             strcpy(display + FSTPOS, display + SNDPOS);
  470.             display[NXTOLAST + dp] =  *((char *) calc[v].ob_spec);
  471.             if (!dp)
  472.                 display[VERYLAST] = '.';
  473.             nd++;
  474.                         }
  475.         rdisp();
  476.         }
  477.  
  478.     op = MENTRY;
  479.  
  480.  
  481. }    /********* end of do_entry(v) ****************************************/
  482.  
  483.  
  484. /******************************************************************************
  485.             EVALUATE A SUBEXPRESSION
  486. ******************************************************************************/
  487.  
  488. evaluate()
  489. {
  490.  
  491.     float opd1, opd2;
  492.     char oper;
  493.  
  494.     opd2 = pop_opd();    /* pop first operand (note reverse order) */
  495.     opd1 = pop_opd();    /* pop second operand */
  496.     oper = pop_opr();    /* pop operator */
  497.                 
  498.     switch(oper)    {
  499.         
  500.         case (PLUS):
  501.             push_opd(opd1 + opd2);
  502.             break;
  503.         case (MINUS):
  504.             push_opd(opd1 - opd2);
  505.             break;
  506.         /* handle "divide by 0" errors here */
  507.         case (DIV):
  508.             if (opd2 == 0)
  509.                 push_opd((double) 0);
  510.             else
  511.                 push_opd(opd1 / opd2);
  512.             break;
  513.         case (MULT):
  514.             push_opd(opd1 * opd2);
  515.             break;
  516.  
  517.             } /* end of switch(oper) ****/
  518.  
  519.     
  520.     frame(opd_stack[opdsp - 1]);    /* display result */
  521.     rdisp();
  522.  
  523.  
  524. } /*********  end of evaluate() **********************************************/
  525.  
  526.  
  527. /******************************************************************************
  528.         FORMAT DOUBLE FOR DISPLAY
  529. ******************************************************************************/
  530.  
  531.  
  532. frame(f)
  533. double f;
  534. {
  535.  
  536.     char buf[60];        /* 1.e+37 + 11 precision = 49 */
  537.     
  538.     sprintf(buf, "%8.6f", f);    /* convert to ASCII float format */
  539.  
  540.     if (ezdp(buf))    {        /* If <= 12 chars, minus tr. 0's... */
  541.  
  542.         strcpy(display, "         ");    /* clear display */
  543.         display[SIGN] = (buf[SIGN] == '-') ? '-' : ' ';
  544.         strcpy(display + MAX_DISP - strlen(buf) + (f < 0),
  545.             buf + (f < 0));
  546.             }
  547.  
  548.     else
  549.         sprintf(display, "%.1e", f);
  550.  
  551. }
  552.  
  553.  
  554. /******************************************************************************
  555.             ELIMINATE TRAILING ZEROS IN BUFFER
  556. ******************************************************************************/
  557.  
  558.  
  559. ezdp(s)
  560. char *s;
  561. {
  562.  
  563.     int i = strlen(s) - 1;
  564.  
  565.     while (s[i] == '0')
  566.         s[i--] = '\0';
  567.  
  568.     return(strlen(s) <= MAX_DISP);
  569.  
  570. }
  571.  
  572.  
  573. /******************************************************************************
  574.         ELIMINATE BLANKS FROM BUFFER FOR ATOF()
  575. ******************************************************************************/
  576.  
  577.  
  578. char *noblanks()
  579. {
  580.  
  581.     static char buf[MAX_DISP];
  582.     int i = 0, j = 0;
  583.  
  584.     for ( ; j < MAX_DISP; j++)
  585.         if (display[j] != ' ')
  586.             buf[i++] = display[j];
  587.  
  588.     buf[i] = '\0';
  589.     return(buf);
  590.  
  591.  
  592. }
  593.