2 Copyright © 1995-2013, The AROS Development Team. All rights reserved.
6 1. PUBSCREEN <name>: the name of the public screen to open the window on
7 2. TAPE <filename>: the name of a file to record the user interactions
10 #include <exec/types.h>
12 #include <intuition/intuition.h>
13 #include <intuition/classes.h>
14 #include <libraries/mui.h>
16 #include <proto/exec.h>
17 #include <proto/dos.h>
18 #include <proto/intuition.h>
19 #include <proto/locale.h>
20 #include <proto/alib.h>
21 #include <proto/muimaster.h>
22 #include <proto/utility.h>
28 const char *version
= "$VER: Calculator 1.4 (08.08.2013) © AROS Dev Team";
30 #define ARG_TEMPLATE "PUBSCREEN,TAPE/K"
31 enum {ARG_PUBSCREEN
,ARG_TAPE
,NUM_ARGS
};
33 /* The pattern of the tape name in case we log to a RAW: window */
34 #define RAW_TAPE_NAME "RAW:%d/%d/%d/%d/Calculator Tape/INACTIVE/SCREEN%s"
60 #define NUM_BUTTONS 20
61 #define DECIMAL_BUTTON_INDEX 16
70 struct CalcButtonInfo BUTTONS
[] =
72 {"7", BTYPE_7
, '7'}, {"8", BTYPE_8
, '8'}, {"9", BTYPE_9
, '9'}, {"CA", BTYPE_CA
, 'A'}, {"CE", BTYPE_CE
, 'E'},
73 {"4", BTYPE_4
, '4'}, {"5", BTYPE_5
, '5'}, {"6", BTYPE_6
, '6'}, {"*", BTYPE_MUL
, '*'}, {":", BTYPE_DIV
, ':'},
74 {"1", BTYPE_1
, '1'}, {"2", BTYPE_2
, '2'}, {"3", BTYPE_3
, '3'}, {"+", BTYPE_ADD
, '+'}, {"-", BTYPE_SUB
, '-'},
75 {"0", BTYPE_0
, '0'}, {".", BTYPE_COMMA
, '.'}, {"<<", BTYPE_BS
, 8}, {"+/-", BTYPE_SIGN
, 's'}, {"=", BTYPE_EQU
, '='}
79 * Most of the application state is local or in BOOPSI objects.
80 * The only global state is to communicate the command line arguments
82 static char pubscrname
[256];
83 static char tapename
[256];
86 /**********************************************************************
88 **********************************************************************/
90 #define TAPEA_FILEHANDLE (TAG_USER + 20)
91 #define TAPEM_NEWLINE (TAG_USER + 21)
92 #define TAPEM_PRINT_LVAL (TAG_USER + 22)
93 #define TAPEM_PRINT_RVAL (TAG_USER + 23)
94 #define TAPEM_PRINT_RESULT (TAG_USER + 24)
101 struct MUIMP_PrintLval
103 STACKED ULONG MethodID
;
104 STACKED
const char *str
;
107 struct MUIMP_PrintRval
109 STACKED ULONG MethodID
;
110 STACKED
char operator;
111 STACKED
const char *str
;
114 struct MUIMP_PrintResult
116 STACKED ULONG MethodID
;
117 STACKED
const char *str
;
120 IPTR
mNewTape(struct IClass
*cl
, Object
*obj
, struct opSet
*msg
)
122 struct TagItem
*tagListState
= msg
->ops_AttrList
, *tag
;
123 Object
*instance
= (Object
*) DoSuperMethodA(cl
, obj
, (APTR
) msg
);
124 struct TapeData
*data
= INST_DATA(cl
, instance
);
125 data
->tapefh
= BNULL
;
127 while ((tag
= (struct TagItem
*) NextTagItem(&tagListState
)))
131 case TAPEA_FILEHANDLE
:
132 data
->tapefh
= (BPTR
) tag
->ti_Data
;
138 return (IPTR
) instance
;
141 IPTR
mDisposeTape(struct IClass
*cl
, Object
*obj
, struct opSet
*msg
)
143 struct TapeData
*data
= INST_DATA(cl
, obj
);
144 if (data
->tapefh
) Close(data
->tapefh
);
145 return DoSuperMethodA(cl
, obj
, (APTR
) msg
);
148 IPTR
mTapeNewline(struct IClass
*cl
, Object
*obj
, APTR msg
)
150 struct TapeData
*data
= INST_DATA(cl
, obj
);
151 if (data
->tapefh
) FPutC(data
->tapefh
, '\n');
155 IPTR
mTapePrintLval(struct IClass
*cl
, Object
*obj
, struct MUIMP_PrintLval
*msg
)
157 struct TapeData
*data
= INST_DATA(cl
, obj
);
160 FPutC(data
->tapefh
, '\t');
161 FPuts(data
->tapefh
, msg
->str
);
162 FPutC(data
->tapefh
, '\n');
168 IPTR
mTapePrintRval(struct IClass
*cl
, Object
*obj
, struct MUIMP_PrintRval
*msg
)
170 struct TapeData
*data
= INST_DATA(cl
, obj
);
173 FPutC(data
->tapefh
, msg
->operator);
174 FPutC(data
->tapefh
, '\t');
175 FPuts(data
->tapefh
, msg
->str
);
176 FPutC(data
->tapefh
, '\n');
182 IPTR
mTapePrintResult(struct IClass
*cl
, Object
*obj
, struct MUIMP_PrintResult
*msg
)
184 struct TapeData
*data
= INST_DATA(cl
, obj
);
187 FPuts(data
->tapefh
, "=\t");
188 FPuts(data
->tapefh
, msg
->str
);
189 FPutC(data
->tapefh
, '\n');
195 ULONG
mSet(struct IClass
*cl
, Object
*obj
, struct opSet
*msg
)
197 struct TagItem
*tagListState
= msg
->ops_AttrList
, *tag
;
198 struct TapeData
*data
= INST_DATA(cl
, obj
);
200 while ((tag
= (struct TagItem
*) NextTagItem(&tagListState
)))
204 case TAPEA_FILEHANDLE
:
205 data
->tapefh
= (BPTR
) tag
->ti_Data
;
214 BOOPSI_DISPATCHER(IPTR
, TapeDispatcher
, cl
, obj
, msg
)
216 switch (msg
->MethodID
)
218 case OM_NEW
: return mNewTape(cl
, obj
, (APTR
) msg
);
219 case OM_DISPOSE
: return mDisposeTape(cl
, obj
, (APTR
) msg
);
220 case OM_SET
: return mSet(cl
, obj
, (APTR
) msg
);
222 case TAPEM_NEWLINE
: return mTapeNewline(cl
, obj
, (APTR
) msg
);
223 case TAPEM_PRINT_LVAL
: return mTapePrintLval(cl
, obj
, (struct MUIMP_PrintLval
*) msg
);
224 case TAPEM_PRINT_RVAL
: return mTapePrintRval(cl
, obj
, (struct MUIMP_PrintRval
*) msg
);
225 case TAPEM_PRINT_RESULT
: return mTapePrintResult(cl
, obj
, (struct MUIMP_PrintResult
*) msg
);
227 return DoSuperMethodA(cl
, obj
, msg
);
229 BOOPSI_DISPATCHER_END
231 static Class
*make_tape_class(void)
234 cl
= MakeClass(NULL
, ROOTCLASS
, NULL
, sizeof(struct TapeData
), 0);
235 if (cl
) cl
->cl_Dispatcher
.h_Entry
= TapeDispatcher
;
239 /**********************************************************************
240 Calculator BOOPSI class
241 **********************************************************************/
243 #define MAX_DIGITS 13
244 #define OM_ADD_KEY (TAG_USER + 30)
245 #define CALCA_DISPLAY (TAG_USER + 31)
246 #define CALCA_TAPE (TAG_USER + 32)
247 #define CALCA_DECIMAL_POINT (TAG_USER + 33)
249 #define EDIT_BUFFER_SIZE MAX_DIGITS + 2 /* space for '-' sign and terminator byte */
250 const char *INITIAL_DISPLAY
= "\033r0";
251 enum { STATE_LEFTVAL
, STATE_OP
, STATE_RIGHTVAL
, STATE_EQU
};
253 struct CalculatorData
255 char edit_buffer
[EDIT_BUFFER_SIZE
];
260 double lvalue
, rvalue
;
262 Object
*display
, *tape
;
267 STACKED ULONG MethodID
;
271 static char op2char(int op
)
274 case BTYPE_MUL
: return '*';
275 case BTYPE_DIV
: return '*';
276 case BTYPE_SUB
: return '-';
277 case BTYPE_ADD
: return '+';
282 static void clear_edit_buffer(struct CalculatorData
*data
)
284 memset(data
->edit_buffer
, 0, EDIT_BUFFER_SIZE
);
285 data
->num_digits
= 0;
288 IPTR
mNewCalc(struct IClass
*cl
, Object
*obj
, struct opSet
*msg
)
290 struct TagItem
*tagListState
= msg
->ops_AttrList
, *tag
;
291 Object
*instance
= (Object
*) DoSuperMethodA(cl
, obj
, (APTR
) msg
);
292 struct CalculatorData
*data
= INST_DATA(cl
, instance
);
293 clear_edit_buffer(data
);
294 data
->display
= NULL
;
296 data
->state
= STATE_LEFTVAL
;
297 data
->decimal_point
= '.';
299 while ((tag
= (struct TagItem
*) NextTagItem(&tagListState
)))
304 data
->display
= (Object
*) tag
->ti_Data
;
307 data
->tape
= (Object
*) tag
->ti_Data
;
309 case CALCA_DECIMAL_POINT
:
310 data
->decimal_point
= (char) tag
->ti_Data
;
316 return (IPTR
) instance
;
319 IPTR
mDisposeCalc(struct IClass
*cl
, Object
*obj
, struct opSet
*msg
)
321 return DoSuperMethodA(cl
, obj
, (APTR
) msg
);
324 static BOOL
is_operator(int btype
)
326 return btype
>= BTYPE_MUL
&& btype
<= BTYPE_ADD
;
329 static BOOL
can_insert_comma(struct CalculatorData
*data
)
331 if (data
->num_digits
== 0) return TRUE
;
335 for (i
= 0; i
< data
->num_digits
; i
++)
337 if (data
->edit_buffer
[i
] == '.') return FALSE
;
343 static double eval_result(double lvalue
, double rvalue
, int op
)
347 case BTYPE_MUL
: return lvalue
* rvalue
;
348 case BTYPE_DIV
: return lvalue
/ rvalue
;
349 case BTYPE_SUB
: return lvalue
- rvalue
;
350 case BTYPE_ADD
: return lvalue
+ rvalue
;
356 static void localize_buffer(char *buffer
, char decimal_point
, int from
, int to
)
359 for (i
= from
; i
< to
; i
++)
361 if (buffer
[i
] == '.')
363 buffer
[i
] = decimal_point
;
369 * Rendering is centralized in this function, by copying
370 * the edit buffer into and manipulating the display buffer.
371 * MUI/Zune's text field right-justifies text through the inclusion
372 * of the "Esc-r" control sequence.
374 static void display_state(struct CalculatorData
*data
)
376 /* add 2 extra chars: Esc+'r' */
377 static char display_buffer
[EDIT_BUFFER_SIZE
+ 2];
379 if (data
->num_digits
== 0)
381 SetAttrs(data
->display
, MUIA_Text_Contents
, INITIAL_DISPLAY
, TAG_DONE
);
385 memset(display_buffer
, 0, EDIT_BUFFER_SIZE
+ 2);
386 display_buffer
[0] = '\033';
387 display_buffer
[1] = 'r';
388 memcpy(display_buffer
+ 2, data
->edit_buffer
, data
->num_digits
);
389 localize_buffer(display_buffer
, data
->decimal_point
, 2, MAX_DIGITS
+ 3);
390 SetAttrs(data
->display
, MUIA_Text_Contents
, display_buffer
, TAG_DONE
);
394 static const char *localize_display(struct CalculatorData
*data
)
396 static char buffer
[EDIT_BUFFER_SIZE
];
397 memset(buffer
, 0, MAX_DIGITS
+ 1);
398 memcpy(buffer
, data
->edit_buffer
, data
->num_digits
);
399 localize_buffer(buffer
, data
->decimal_point
, 0, MAX_DIGITS
+ 1);
403 static void toggle_sign(struct CalculatorData
*data
)
405 BOOL sign_found
= FALSE
;
407 if (data
->state
== STATE_LEFTVAL
) data
->lvalue
= -data
->lvalue
;
408 else if (data
->state
== STATE_RIGHTVAL
) data
->rvalue
= -data
->rvalue
;
410 for (i
= 0; i
< EDIT_BUFFER_SIZE
; i
++)
412 if (data
->edit_buffer
[i
] == '-')
421 /* eliminate the sign by shifting to the left */
423 memmove(data
->edit_buffer
, data
->edit_buffer
+ 1, data
->num_digits
);
424 data
->edit_buffer
[data
->num_digits
] = 0;
428 /* add sign by shifting to the right and inserting - */
429 memmove(data
->edit_buffer
+ 1, data
->edit_buffer
, data
->num_digits
);
431 data
->edit_buffer
[0] = '-';
432 data
->edit_buffer
[data
->num_digits
] = 0;
438 * This method implements the main logic of the calculator by performing transitions
439 * of a state machine.
441 IPTR
mAddCalcKey(struct IClass
*cl
, Object
*obj
, struct MUIMP_CalcKey
*msg
)
443 struct CalculatorData
*data
= INST_DATA(cl
, obj
);
444 if (msg
->btype
<= BTYPE_9
&& data
->num_digits
< MAX_DIGITS
)
446 if (data
->state
== STATE_OP
)
448 data
->state
= STATE_RIGHTVAL
;
449 clear_edit_buffer(data
);
451 if (data
->state
== STATE_EQU
)
453 data
->state
= STATE_LEFTVAL
;
454 clear_edit_buffer(data
);
456 char digit
= '0' + msg
->btype
;
457 data
->edit_buffer
[data
->num_digits
++] = digit
;
461 else if (msg
->btype
== BTYPE_COMMA
&& can_insert_comma(data
))
463 data
->edit_buffer
[data
->num_digits
++] = '.';
466 else if (is_operator(msg
->btype
))
468 if (data
->state
== STATE_LEFTVAL
|| data
->state
== STATE_EQU
)
470 data
->lvalue
= strtod(data
->edit_buffer
, NULL
);
471 data
->state
= STATE_OP
;
472 data
->op
= msg
->btype
;
473 if (data
->tape
) DoMethod(data
->tape
, TAPEM_PRINT_LVAL
, localize_display(data
));
476 else if (msg
->btype
== BTYPE_EQU
)
478 if (data
->tape
) DoMethod(data
->tape
, TAPEM_PRINT_RVAL
, op2char(data
->op
), localize_display(data
));
480 data
->rvalue
= strtod(data
->edit_buffer
, NULL
);
481 data
->state
= STATE_EQU
;
482 data
->lvalue
= eval_result(data
->lvalue
, data
->rvalue
, data
->op
);
483 snprintf(data
->edit_buffer
, MAX_DIGITS
, "%f", data
->lvalue
);
484 /* note that there is no strnlen() in AROS !!! */
485 data
->num_digits
= strlen(data
->edit_buffer
);
488 if (data
->tape
) DoMethod(data
->tape
, TAPEM_PRINT_RESULT
, localize_display(data
));
490 else if (msg
->btype
== BTYPE_CA
)
494 data
->op
= BTYPE_ADD
;
495 data
->state
= STATE_LEFTVAL
;
496 clear_edit_buffer(data
);
499 if (data
->tape
) DoMethod(data
->tape
, TAPEM_NEWLINE
);
501 else if (msg
->btype
== BTYPE_CE
&&
502 (data
->state
== STATE_LEFTVAL
|| data
->state
== STATE_RIGHTVAL
))
504 clear_edit_buffer(data
);
508 else if (msg
->btype
== BTYPE_SIGN
&& data
->state
!= STATE_OP
)
512 else if (msg
->btype
== BTYPE_BS
&&
513 (data
->state
== STATE_LEFTVAL
|| data
->state
== STATE_RIGHTVAL
) &&
514 data
->num_digits
> 0)
516 data
->edit_buffer
[--data
->num_digits
] = 0;
522 BOOPSI_DISPATCHER(IPTR
, CalculatorDispatcher
, cl
, obj
, msg
)
524 switch (msg
->MethodID
)
526 case OM_NEW
: return mNewCalc(cl
, obj
, (APTR
) msg
);
527 case OM_DISPOSE
: return mDisposeCalc(cl
, obj
, (APTR
) msg
);
528 case OM_ADD_KEY
: return (IPTR
) mAddCalcKey(cl
, obj
, (struct MUIMP_CalcKey
*) msg
);
530 return DoSuperMethodA(cl
, obj
, msg
);
532 BOOPSI_DISPATCHER_END
534 static Class
*make_calculator_class(void)
537 cl
= MakeClass(NULL
, ROOTCLASS
, NULL
, sizeof(struct CalculatorData
), 0);
538 if (cl
) cl
->cl_Dispatcher
.h_Entry
= CalculatorDispatcher
;
542 /**********************************************************************
544 **********************************************************************/
546 static void cleanup(char *msg
)
551 fprintf(stderr
, "Calculator: %s\n", msg
);
561 static void dos_error(void)
563 static char tempstring
[256];
564 Fault(IoErr(), 0, tempstring
, 255);
568 static char retrieve_decimal_point(void)
573 if ((loc
= OpenLocale(0)))
575 result
= loc
->loc_DecimalPoint
[0];
581 static void get_arguments(void)
583 struct RDArgs
*rdargs
;
587 for (i
= 0; i
< NUM_ARGS
; i
++) args
[i
] = (IPTR
) NULL
;
589 if (!(rdargs
= ReadArgs(ARG_TEMPLATE
, (IPTR
*) args
,0))) dos_error();
591 if (args
[ARG_PUBSCREEN
]) {
592 strncpy(pubscrname
, (const char *) args
[ARG_PUBSCREEN
], 255);
598 strncpy(tapename
, (const char *) args
[ARG_TAPE
], 255);
600 if (rdargs
) FreeArgs(rdargs
);
603 static void open_raw_tape_if_needed(Object
*window
, Object
*obj_tape
)
605 if (use_tape
&& !strlen(tapename
))
611 GetAttr(MUIA_Window_Window
, window
, (IPTR
*) &win
);
612 w
= win
->Width
* 5 / 4;
617 if (x
> (win
->WScreen
->Width
- (x
+ w
))) x
-= w
;
618 else x
+= win
->WScreen
->Width
;
620 snprintf(tapename
, 255, RAW_TAPE_NAME
, x
, y
, w
, h
, pubscrname
);
621 tapefh
= Open(tapename
, MODE_NEWFILE
);
622 SetAttrs(obj_tape
, TAPEA_FILEHANDLE
, tapefh
);
628 Class
*cl_calc
= NULL
, *cl_tape
= NULL
;
629 Object
*app
= NULL
, *window
= NULL
, *display
= NULL
, *button
[NUM_BUTTONS
],
630 *obj_calc
, *obj_tape
;
631 char decimal_point
, decimal_label
[2];
632 struct Screen
*pub_screen
= NULL
;
637 decimal_point
= retrieve_decimal_point();
638 snprintf(decimal_label
, 2, "%c", decimal_point
);
640 display
= TextObject
,
642 MUIA_Text_Contents
, INITIAL_DISPLAY
,
643 MUIA_ShortHelp
, "Display",
646 cl_tape
= make_tape_class();
648 if (use_tape
&& strlen(tapename
)) {
649 tapefh
= Open(tapename
, MODE_NEWFILE
);
653 obj_tape
= NewObject(cl_tape
, NULL
, TAPEA_FILEHANDLE
, tapefh
, TAG_DONE
);
657 obj_tape
= NewObject(cl_tape
, NULL
, TAG_DONE
);
660 cl_calc
= make_calculator_class();
661 obj_calc
= NewObject(cl_calc
, NULL
,
662 CALCA_DISPLAY
, display
,
663 CALCA_TAPE
, obj_tape
,
664 CALCA_DECIMAL_POINT
, decimal_point
,
667 for (i
= 0; i
< NUM_BUTTONS
; i
++)
669 button
[i
] = (i
!= DECIMAL_BUTTON_INDEX
) ?
670 SimpleButton(BUTTONS
[i
].label
) : SimpleButton(decimal_label
);
671 if (BUTTONS
[i
].shortcut
)
673 SetAttrs(button
[i
], MUIA_ControlChar
, BUTTONS
[i
].shortcut
, TAG_DONE
);
677 if (strlen(pubscrname
))
679 pub_screen
= LockPubScreen((CONST_STRPTR
) pubscrname
);
682 printf("Can't lock public screen '%s' -> fallback to Wanderer!\n", pubscrname
);
683 memset(pubscrname
, 0, 256);
684 strcpy(pubscrname
, "Workbench");
685 pub_screen
= LockPubScreen((CONST_STRPTR
) pubscrname
);
689 app
= ApplicationObject
,
690 MUIA_Application_Title
, "Calculator",
691 MUIA_Application_Version
, "1.4",
692 MUIA_Application_Copyright
, "©2007-2013, AROS Dev Team",
693 MUIA_Application_Author
, "AROS Team",
694 MUIA_Application_Description
, "Simple desktop calculator",
695 MUIA_Application_Base
, "calculator",
696 SubWindow
, window
= WindowObject
,
697 MUIA_Window_Title
, "Calculator",
698 MUIA_Window_ID
, MAKE_ID('C', 'A', 'L', 'C'),
699 MUIA_Window_AppWindow
, TRUE
,
700 MUIA_Window_Screen
, pub_screen
,
701 WindowContents
, VGroup
,
704 Child
, HGroup
, GroupSpacing(5), MUIA_Group_SameWidth
, TRUE
,
712 Child
, HGroup
, GroupSpacing(5), MUIA_Group_SameWidth
, TRUE
,
720 Child
, HGroup
, GroupSpacing(5), MUIA_Group_SameWidth
, TRUE
,
728 Child
, HGroup
, GroupSpacing(5), MUIA_Group_SameWidth
, TRUE
,
740 DoMethod(window
, MUIM_Notify
, MUIA_Window_CloseRequest
, TRUE
,
741 app
, 2, MUIM_Application_ReturnID
,
742 MUIV_Application_ReturnID_Quit
);
744 for (i
= 0; i
< NUM_BUTTONS
; i
++)
746 DoMethod(button
[i
], MUIM_Notify
, MUIA_Pressed
, FALSE
,
747 obj_calc
, 2, OM_ADD_KEY
, BUTTONS
[i
].btype
);
750 SetAttrs(window
, MUIA_Window_Open
, TRUE
, TAG_DONE
);
751 open_raw_tape_if_needed(window
, obj_tape
);
753 if (pub_screen
) UnlockPubScreen(0, pub_screen
);
759 id
= DoMethod(app
, MUIM_Application_NewInput
, (IPTR
) &sigs
);
763 case MUIV_Application_ReturnID_Quit
:
771 sigs
= Wait(sigs
| SIGBREAKF_CTRL_C
);
772 if (sigs
& SIGBREAKF_CTRL_C
) break;
776 set((APTR
) window
, MUIA_Window_Open
, FALSE
);
777 MUI_DisposeObject(app
);
779 DisposeObject(obj_calc
);
780 DisposeObject(obj_tape
);