2 Copyright © 1995-2018, 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.5 (31.05.2018) © 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 #define CALC_TAPENAME_MAX 320
83 static char pubscrname
[256];
84 static char tapename
[CALC_TAPENAME_MAX
];
87 /**********************************************************************
89 **********************************************************************/
91 #define TAPEA_FILEHANDLE (TAG_USER + 20)
92 #define TAPEM_NEWLINE (TAG_USER + 21)
93 #define TAPEM_PRINT_LVAL (TAG_USER + 22)
94 #define TAPEM_PRINT_RVAL (TAG_USER + 23)
95 #define TAPEM_PRINT_RESULT (TAG_USER + 24)
102 struct MUIMP_PrintLval
104 STACKED ULONG MethodID
;
105 STACKED
const char *str
;
108 struct MUIMP_PrintRval
110 STACKED ULONG MethodID
;
111 STACKED
char operator;
112 STACKED
const char *str
;
115 struct MUIMP_PrintResult
117 STACKED ULONG MethodID
;
118 STACKED
const char *str
;
121 IPTR
mNewTape(struct IClass
*cl
, Object
*obj
, struct opSet
*msg
)
123 struct TagItem
*tagListState
= msg
->ops_AttrList
, *tag
;
124 Object
*instance
= (Object
*) DoSuperMethodA(cl
, obj
, (APTR
) msg
);
125 struct TapeData
*data
= INST_DATA(cl
, instance
);
126 data
->tapefh
= BNULL
;
128 while ((tag
= (struct TagItem
*) NextTagItem(&tagListState
)))
132 case TAPEA_FILEHANDLE
:
133 data
->tapefh
= (BPTR
) tag
->ti_Data
;
139 return (IPTR
) instance
;
142 IPTR
mDisposeTape(struct IClass
*cl
, Object
*obj
, struct opSet
*msg
)
144 struct TapeData
*data
= INST_DATA(cl
, obj
);
145 if (data
->tapefh
) Close(data
->tapefh
);
146 return DoSuperMethodA(cl
, obj
, (APTR
) msg
);
149 IPTR
mTapeNewline(struct IClass
*cl
, Object
*obj
, APTR msg
)
151 struct TapeData
*data
= INST_DATA(cl
, obj
);
152 if (data
->tapefh
) FPutC(data
->tapefh
, '\n');
156 IPTR
mTapePrintLval(struct IClass
*cl
, Object
*obj
, struct MUIMP_PrintLval
*msg
)
158 struct TapeData
*data
= INST_DATA(cl
, obj
);
161 FPutC(data
->tapefh
, '\t');
162 FPuts(data
->tapefh
, msg
->str
);
163 FPutC(data
->tapefh
, '\n');
169 IPTR
mTapePrintRval(struct IClass
*cl
, Object
*obj
, struct MUIMP_PrintRval
*msg
)
171 struct TapeData
*data
= INST_DATA(cl
, obj
);
174 FPutC(data
->tapefh
, msg
->operator);
175 FPutC(data
->tapefh
, '\t');
176 FPuts(data
->tapefh
, msg
->str
);
177 FPutC(data
->tapefh
, '\n');
183 IPTR
mTapePrintResult(struct IClass
*cl
, Object
*obj
, struct MUIMP_PrintResult
*msg
)
185 struct TapeData
*data
= INST_DATA(cl
, obj
);
188 FPuts(data
->tapefh
, "=\t");
189 FPuts(data
->tapefh
, msg
->str
);
190 FPutC(data
->tapefh
, '\n');
196 ULONG
mSet(struct IClass
*cl
, Object
*obj
, struct opSet
*msg
)
198 struct TagItem
*tagListState
= msg
->ops_AttrList
, *tag
;
199 struct TapeData
*data
= INST_DATA(cl
, obj
);
201 while ((tag
= (struct TagItem
*) NextTagItem(&tagListState
)))
205 case TAPEA_FILEHANDLE
:
206 data
->tapefh
= (BPTR
) tag
->ti_Data
;
215 BOOPSI_DISPATCHER(IPTR
, TapeDispatcher
, cl
, obj
, msg
)
217 switch (msg
->MethodID
)
219 case OM_NEW
: return mNewTape(cl
, obj
, (APTR
) msg
);
220 case OM_DISPOSE
: return mDisposeTape(cl
, obj
, (APTR
) msg
);
221 case OM_SET
: return mSet(cl
, obj
, (APTR
) msg
);
223 case TAPEM_NEWLINE
: return mTapeNewline(cl
, obj
, (APTR
) msg
);
224 case TAPEM_PRINT_LVAL
: return mTapePrintLval(cl
, obj
, (struct MUIMP_PrintLval
*) msg
);
225 case TAPEM_PRINT_RVAL
: return mTapePrintRval(cl
, obj
, (struct MUIMP_PrintRval
*) msg
);
226 case TAPEM_PRINT_RESULT
: return mTapePrintResult(cl
, obj
, (struct MUIMP_PrintResult
*) msg
);
228 return DoSuperMethodA(cl
, obj
, msg
);
230 BOOPSI_DISPATCHER_END
232 static Class
*make_tape_class(void)
235 cl
= MakeClass(NULL
, ROOTCLASS
, NULL
, sizeof(struct TapeData
), 0);
236 if (cl
) cl
->cl_Dispatcher
.h_Entry
= TapeDispatcher
;
240 /**********************************************************************
241 Calculator BOOPSI class
242 **********************************************************************/
244 #define MAX_DIGITS 13
245 #define OM_ADD_KEY (TAG_USER + 30)
246 #define CALCA_DISPLAY (TAG_USER + 31)
247 #define CALCA_TAPE (TAG_USER + 32)
248 #define CALCA_DECIMAL_POINT (TAG_USER + 33)
250 #define EDIT_BUFFER_SIZE MAX_DIGITS + 2 /* space for '-' sign and terminator byte */
251 const char *INITIAL_DISPLAY
= "\033r0";
252 enum { STATE_LEFTVAL
, STATE_OP
, STATE_RIGHTVAL
, STATE_EQU
};
254 struct CalculatorData
256 char edit_buffer
[EDIT_BUFFER_SIZE
];
261 double lvalue
, rvalue
;
263 Object
*display
, *tape
;
268 STACKED ULONG MethodID
;
272 static char op2char(int op
)
275 case BTYPE_MUL
: return '*';
276 case BTYPE_DIV
: return '*';
277 case BTYPE_SUB
: return '-';
278 case BTYPE_ADD
: return '+';
283 static void clear_edit_buffer(struct CalculatorData
*data
)
285 memset(data
->edit_buffer
, 0, EDIT_BUFFER_SIZE
);
286 data
->num_digits
= 0;
289 IPTR
mNewCalc(struct IClass
*cl
, Object
*obj
, struct opSet
*msg
)
291 struct TagItem
*tagListState
= msg
->ops_AttrList
, *tag
;
292 Object
*instance
= (Object
*) DoSuperMethodA(cl
, obj
, (APTR
) msg
);
293 struct CalculatorData
*data
= INST_DATA(cl
, instance
);
294 clear_edit_buffer(data
);
295 data
->display
= NULL
;
297 data
->state
= STATE_LEFTVAL
;
298 data
->decimal_point
= '.';
300 while ((tag
= (struct TagItem
*) NextTagItem(&tagListState
)))
305 data
->display
= (Object
*) tag
->ti_Data
;
308 data
->tape
= (Object
*) tag
->ti_Data
;
310 case CALCA_DECIMAL_POINT
:
311 data
->decimal_point
= (char) tag
->ti_Data
;
317 return (IPTR
) instance
;
320 IPTR
mDisposeCalc(struct IClass
*cl
, Object
*obj
, struct opSet
*msg
)
322 return DoSuperMethodA(cl
, obj
, (APTR
) msg
);
325 static BOOL
is_operator(int btype
)
327 return btype
>= BTYPE_MUL
&& btype
<= BTYPE_ADD
;
330 static BOOL
can_insert_comma(struct CalculatorData
*data
)
332 if (data
->num_digits
== 0) return TRUE
;
336 for (i
= 0; i
< data
->num_digits
; i
++)
338 if (data
->edit_buffer
[i
] == '.') return FALSE
;
344 static double eval_result(double lvalue
, double rvalue
, int op
)
348 case BTYPE_MUL
: return lvalue
* rvalue
;
349 case BTYPE_DIV
: return lvalue
/ rvalue
;
350 case BTYPE_SUB
: return lvalue
- rvalue
;
351 case BTYPE_ADD
: return lvalue
+ rvalue
;
357 static void localize_buffer(char *buffer
, char decimal_point
, int from
, int to
)
360 for (i
= from
; i
< to
; i
++)
362 if (buffer
[i
] == '.')
364 buffer
[i
] = decimal_point
;
370 * Rendering is centralized in this function, by copying
371 * the edit buffer into and manipulating the display buffer.
372 * MUI/Zune's text field right-justifies text through the inclusion
373 * of the "Esc-r" control sequence.
375 static void display_state(struct CalculatorData
*data
)
377 /* add 2 extra chars: Esc+'r' */
378 static char display_buffer
[EDIT_BUFFER_SIZE
+ 2];
380 if (data
->num_digits
== 0)
382 SetAttrs(data
->display
, MUIA_Text_Contents
, INITIAL_DISPLAY
, TAG_DONE
);
386 memset(display_buffer
, 0, EDIT_BUFFER_SIZE
+ 2);
387 display_buffer
[0] = '\033';
388 display_buffer
[1] = 'r';
389 memcpy(display_buffer
+ 2, data
->edit_buffer
, data
->num_digits
);
390 localize_buffer(display_buffer
, data
->decimal_point
, 2, MAX_DIGITS
+ 3);
391 SetAttrs(data
->display
, MUIA_Text_Contents
, display_buffer
, TAG_DONE
);
395 static const char *localize_display(struct CalculatorData
*data
)
397 static char buffer
[EDIT_BUFFER_SIZE
];
398 memset(buffer
, 0, MAX_DIGITS
+ 1);
399 memcpy(buffer
, data
->edit_buffer
, data
->num_digits
);
400 localize_buffer(buffer
, data
->decimal_point
, 0, MAX_DIGITS
+ 1);
404 static void toggle_sign(struct CalculatorData
*data
)
406 BOOL sign_found
= FALSE
;
408 if (data
->state
== STATE_LEFTVAL
) data
->lvalue
= -data
->lvalue
;
409 else if (data
->state
== STATE_RIGHTVAL
) data
->rvalue
= -data
->rvalue
;
411 for (i
= 0; i
< EDIT_BUFFER_SIZE
; i
++)
413 if (data
->edit_buffer
[i
] == '-')
422 /* eliminate the sign by shifting to the left */
424 memmove(data
->edit_buffer
, data
->edit_buffer
+ 1, data
->num_digits
);
425 data
->edit_buffer
[data
->num_digits
] = 0;
429 /* add sign by shifting to the right and inserting - */
430 memmove(data
->edit_buffer
+ 1, data
->edit_buffer
, data
->num_digits
);
432 data
->edit_buffer
[0] = '-';
433 data
->edit_buffer
[data
->num_digits
] = 0;
439 * This method implements the main logic of the calculator by performing transitions
440 * of a state machine.
442 IPTR
mAddCalcKey(struct IClass
*cl
, Object
*obj
, struct MUIMP_CalcKey
*msg
)
444 struct CalculatorData
*data
= INST_DATA(cl
, obj
);
445 if (msg
->btype
<= BTYPE_9
&& data
->num_digits
< MAX_DIGITS
)
447 if (data
->state
== STATE_OP
)
449 data
->state
= STATE_RIGHTVAL
;
450 clear_edit_buffer(data
);
452 if (data
->state
== STATE_EQU
)
454 data
->state
= STATE_LEFTVAL
;
455 clear_edit_buffer(data
);
457 char digit
= '0' + msg
->btype
;
458 data
->edit_buffer
[data
->num_digits
++] = digit
;
462 else if (msg
->btype
== BTYPE_COMMA
&& can_insert_comma(data
))
464 data
->edit_buffer
[data
->num_digits
++] = '.';
467 else if (is_operator(msg
->btype
))
469 if (data
->state
== STATE_LEFTVAL
|| data
->state
== STATE_EQU
)
471 data
->lvalue
= strtod(data
->edit_buffer
, NULL
);
472 data
->state
= STATE_OP
;
473 data
->op
= msg
->btype
;
474 if (data
->tape
) DoMethod(data
->tape
, TAPEM_PRINT_LVAL
, localize_display(data
));
477 else if (msg
->btype
== BTYPE_EQU
)
479 if (data
->tape
) DoMethod(data
->tape
, TAPEM_PRINT_RVAL
, op2char(data
->op
), localize_display(data
));
481 data
->rvalue
= strtod(data
->edit_buffer
, NULL
);
482 data
->state
= STATE_EQU
;
483 data
->lvalue
= eval_result(data
->lvalue
, data
->rvalue
, data
->op
);
484 snprintf(data
->edit_buffer
, MAX_DIGITS
, "%f", data
->lvalue
);
485 /* note that there is no strnlen() in AROS !!! */
486 data
->num_digits
= strlen(data
->edit_buffer
);
489 if (data
->tape
) DoMethod(data
->tape
, TAPEM_PRINT_RESULT
, localize_display(data
));
491 else if (msg
->btype
== BTYPE_CA
)
495 data
->op
= BTYPE_ADD
;
496 data
->state
= STATE_LEFTVAL
;
497 clear_edit_buffer(data
);
500 if (data
->tape
) DoMethod(data
->tape
, TAPEM_NEWLINE
);
502 else if (msg
->btype
== BTYPE_CE
&&
503 (data
->state
== STATE_LEFTVAL
|| data
->state
== STATE_RIGHTVAL
))
505 clear_edit_buffer(data
);
509 else if (msg
->btype
== BTYPE_SIGN
&& data
->state
!= STATE_OP
)
513 else if (msg
->btype
== BTYPE_BS
&&
514 (data
->state
== STATE_LEFTVAL
|| data
->state
== STATE_RIGHTVAL
) &&
515 data
->num_digits
> 0)
517 data
->edit_buffer
[--data
->num_digits
] = 0;
523 BOOPSI_DISPATCHER(IPTR
, CalculatorDispatcher
, cl
, obj
, msg
)
525 switch (msg
->MethodID
)
527 case OM_NEW
: return mNewCalc(cl
, obj
, (APTR
) msg
);
528 case OM_DISPOSE
: return mDisposeCalc(cl
, obj
, (APTR
) msg
);
529 case OM_ADD_KEY
: return (IPTR
) mAddCalcKey(cl
, obj
, (struct MUIMP_CalcKey
*) msg
);
531 return DoSuperMethodA(cl
, obj
, msg
);
533 BOOPSI_DISPATCHER_END
535 static Class
*make_calculator_class(void)
538 cl
= MakeClass(NULL
, ROOTCLASS
, NULL
, sizeof(struct CalculatorData
), 0);
539 if (cl
) cl
->cl_Dispatcher
.h_Entry
= CalculatorDispatcher
;
543 /**********************************************************************
545 **********************************************************************/
547 static void cleanup(char *msg
)
552 fprintf(stderr
, "Calculator: %s\n", msg
);
562 static void dos_error(void)
564 static char tempstring
[256];
565 Fault(IoErr(), 0, tempstring
, 255);
569 static char retrieve_decimal_point(void)
574 if ((loc
= OpenLocale(0)))
576 result
= loc
->loc_DecimalPoint
[0];
582 static void get_arguments(void)
584 struct RDArgs
*rdargs
;
588 for (i
= 0; i
< NUM_ARGS
; i
++) args
[i
] = (IPTR
) NULL
;
590 if (!(rdargs
= ReadArgs(ARG_TEMPLATE
, (IPTR
*) args
,0))) dos_error();
592 if (args
[ARG_PUBSCREEN
]) {
593 strncpy(pubscrname
, (const char *) args
[ARG_PUBSCREEN
], 255);
599 strncpy(tapename
, (const char *) args
[ARG_TAPE
], CALC_TAPENAME_MAX
);
601 if (rdargs
) FreeArgs(rdargs
);
604 static void open_raw_tape_if_needed(Object
*window
, Object
*obj_tape
)
606 if (use_tape
&& !strlen(tapename
))
612 GetAttr(MUIA_Window_Window
, window
, (IPTR
*) &win
);
613 w
= win
->Width
* 5 / 4;
618 if (x
> (win
->WScreen
->Width
- (x
+ w
))) x
-= w
;
619 else x
+= win
->WScreen
->Width
;
621 snprintf(tapename
, CALC_TAPENAME_MAX
, RAW_TAPE_NAME
, x
, y
, w
, h
, pubscrname
);
622 tapefh
= Open(tapename
, MODE_NEWFILE
);
623 SetAttrs(obj_tape
, TAPEA_FILEHANDLE
, tapefh
);
629 Class
*cl_calc
= NULL
, *cl_tape
= NULL
;
630 Object
*app
= NULL
, *window
= NULL
, *display
= NULL
, *button
[NUM_BUTTONS
],
631 *obj_calc
, *obj_tape
;
632 char decimal_point
, decimal_label
[2];
633 struct Screen
*pub_screen
= NULL
;
638 decimal_point
= retrieve_decimal_point();
639 snprintf(decimal_label
, 2, "%c", decimal_point
);
641 display
= TextObject
,
643 MUIA_Text_Contents
, INITIAL_DISPLAY
,
644 MUIA_ShortHelp
, "Display",
647 cl_tape
= make_tape_class();
649 if (use_tape
&& strlen(tapename
)) {
650 tapefh
= Open(tapename
, MODE_NEWFILE
);
654 obj_tape
= NewObject(cl_tape
, NULL
, TAPEA_FILEHANDLE
, tapefh
, TAG_DONE
);
658 obj_tape
= NewObject(cl_tape
, NULL
, TAG_DONE
);
661 cl_calc
= make_calculator_class();
662 obj_calc
= NewObject(cl_calc
, NULL
,
663 CALCA_DISPLAY
, display
,
664 CALCA_TAPE
, obj_tape
,
665 CALCA_DECIMAL_POINT
, decimal_point
,
668 for (i
= 0; i
< NUM_BUTTONS
; i
++)
670 button
[i
] = (i
!= DECIMAL_BUTTON_INDEX
) ?
671 SimpleButton(BUTTONS
[i
].label
) : SimpleButton(decimal_label
);
672 if (BUTTONS
[i
].shortcut
)
674 SetAttrs(button
[i
], MUIA_ControlChar
, BUTTONS
[i
].shortcut
, TAG_DONE
);
678 if (strlen(pubscrname
))
680 pub_screen
= LockPubScreen((CONST_STRPTR
) pubscrname
);
683 printf("Can't lock public screen '%s' -> fallback to Wanderer!\n", pubscrname
);
684 memset(pubscrname
, 0, 256);
685 strcpy(pubscrname
, "Workbench");
686 pub_screen
= LockPubScreen((CONST_STRPTR
) pubscrname
);
690 app
= ApplicationObject
,
691 MUIA_Application_Title
, "Calculator",
692 MUIA_Application_Version
, "1.5",
693 MUIA_Application_Copyright
, "©2007-2018, AROS Dev Team",
694 MUIA_Application_Author
, "AROS Team",
695 MUIA_Application_Description
, "Simple desktop calculator",
696 MUIA_Application_Base
, "calculator",
697 SubWindow
, window
= WindowObject
,
698 MUIA_Window_Title
, "Calculator",
699 MUIA_Window_ID
, MAKE_ID('C', 'A', 'L', 'C'),
700 MUIA_Window_AppWindow
, TRUE
,
701 MUIA_Window_Screen
, pub_screen
,
702 WindowContents
, VGroup
,
705 Child
, HGroup
, GroupSpacing(5), MUIA_Group_SameWidth
, TRUE
,
713 Child
, HGroup
, GroupSpacing(5), MUIA_Group_SameWidth
, TRUE
,
721 Child
, HGroup
, GroupSpacing(5), MUIA_Group_SameWidth
, TRUE
,
729 Child
, HGroup
, GroupSpacing(5), MUIA_Group_SameWidth
, TRUE
,
741 DoMethod(window
, MUIM_Notify
, MUIA_Window_CloseRequest
, TRUE
,
742 app
, 2, MUIM_Application_ReturnID
,
743 MUIV_Application_ReturnID_Quit
);
745 for (i
= 0; i
< NUM_BUTTONS
; i
++)
747 DoMethod(button
[i
], MUIM_Notify
, MUIA_Pressed
, FALSE
,
748 obj_calc
, 2, OM_ADD_KEY
, BUTTONS
[i
].btype
);
751 SetAttrs(window
, MUIA_Window_Open
, TRUE
, TAG_DONE
);
752 open_raw_tape_if_needed(window
, obj_tape
);
754 if (pub_screen
) UnlockPubScreen(0, pub_screen
);
760 id
= DoMethod(app
, MUIM_Application_NewInput
, (IPTR
) &sigs
);
764 case MUIV_Application_ReturnID_Quit
:
772 sigs
= Wait(sigs
| SIGBREAKF_CTRL_C
);
773 if (sigs
& SIGBREAKF_CTRL_C
) break;
777 set((APTR
) window
, MUIA_Window_Open
, FALSE
);
778 MUI_DisposeObject(app
);
780 DisposeObject(obj_calc
);
781 DisposeObject(obj_tape
);