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 IPTR
TapeDispatcher(struct IClass
*cl
, Object
*obj
, Msg 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
);
230 static Class
*make_tape_class(void)
233 cl
= MakeClass(NULL
, ROOTCLASS
, NULL
, sizeof(struct TapeData
), 0);
234 if (cl
) cl
->cl_Dispatcher
.h_Entry
= TapeDispatcher
;
238 /**********************************************************************
239 Calculator BOOPSI class
240 **********************************************************************/
242 #define MAX_DIGITS 13
243 #define OM_ADD_KEY (TAG_USER + 30)
244 #define CALCA_DISPLAY (TAG_USER + 31)
245 #define CALCA_TAPE (TAG_USER + 32)
246 #define CALCA_DECIMAL_POINT (TAG_USER + 33)
248 #define EDIT_BUFFER_SIZE MAX_DIGITS + 2 /* space for '-' sign and terminator byte */
249 const char *INITIAL_DISPLAY
= "\033r0";
250 enum { STATE_LEFTVAL
, STATE_OP
, STATE_RIGHTVAL
, STATE_EQU
};
252 struct CalculatorData
254 char edit_buffer
[EDIT_BUFFER_SIZE
];
259 double lvalue
, rvalue
;
261 Object
*display
, *tape
;
266 STACKED ULONG MethodID
;
270 static char op2char(int op
)
273 case BTYPE_MUL
: return '*';
274 case BTYPE_DIV
: return '*';
275 case BTYPE_SUB
: return '-';
276 case BTYPE_ADD
: return '+';
281 static void clear_edit_buffer(struct CalculatorData
*data
)
283 memset(data
->edit_buffer
, 0, EDIT_BUFFER_SIZE
);
284 data
->num_digits
= 0;
287 IPTR
mNewCalc(struct IClass
*cl
, Object
*obj
, struct opSet
*msg
)
289 struct TagItem
*tagListState
= msg
->ops_AttrList
, *tag
;
290 Object
*instance
= (Object
*) DoSuperMethodA(cl
, obj
, (APTR
) msg
);
291 struct CalculatorData
*data
= INST_DATA(cl
, instance
);
292 clear_edit_buffer(data
);
293 data
->display
= NULL
;
295 data
->state
= STATE_LEFTVAL
;
296 data
->decimal_point
= '.';
298 while ((tag
= (struct TagItem
*) NextTagItem(&tagListState
)))
303 data
->display
= (Object
*) tag
->ti_Data
;
306 data
->tape
= (Object
*) tag
->ti_Data
;
308 case CALCA_DECIMAL_POINT
:
309 data
->decimal_point
= (char) tag
->ti_Data
;
315 return (IPTR
) instance
;
318 IPTR
mDisposeCalc(struct IClass
*cl
, Object
*obj
, struct opSet
*msg
)
320 return DoSuperMethodA(cl
, obj
, (APTR
) msg
);
323 static BOOL
is_operator(int btype
)
325 return btype
>= BTYPE_MUL
&& btype
<= BTYPE_ADD
;
328 static BOOL
can_insert_comma(struct CalculatorData
*data
)
330 if (data
->num_digits
== 0) return TRUE
;
334 for (i
= 0; i
< data
->num_digits
; i
++)
336 if (data
->edit_buffer
[i
] == '.') return FALSE
;
342 static double eval_result(double lvalue
, double rvalue
, int op
)
346 case BTYPE_MUL
: return lvalue
* rvalue
;
347 case BTYPE_DIV
: return lvalue
/ rvalue
;
348 case BTYPE_SUB
: return lvalue
- rvalue
;
349 case BTYPE_ADD
: return lvalue
+ rvalue
;
355 static void localize_buffer(char *buffer
, char decimal_point
, int from
, int to
)
358 for (i
= from
; i
< to
; i
++)
360 if (buffer
[i
] == '.')
362 buffer
[i
] = decimal_point
;
368 * Rendering is centralized in this function, by copying
369 * the edit buffer into and manipulating the display buffer.
370 * MUI/Zune's text field right-justifies text through the inclusion
371 * of the "Esc-r" control sequence.
373 static void display_state(struct CalculatorData
*data
)
375 /* add 2 extra chars: Esc+'r' */
376 static char display_buffer
[EDIT_BUFFER_SIZE
+ 2];
378 if (data
->num_digits
== 0)
380 SetAttrs(data
->display
, MUIA_Text_Contents
, INITIAL_DISPLAY
, TAG_DONE
);
384 memset(display_buffer
, 0, EDIT_BUFFER_SIZE
+ 2);
385 display_buffer
[0] = '\033';
386 display_buffer
[1] = 'r';
387 memcpy(display_buffer
+ 2, data
->edit_buffer
, data
->num_digits
);
388 localize_buffer(display_buffer
, data
->decimal_point
, 2, MAX_DIGITS
+ 3);
389 SetAttrs(data
->display
, MUIA_Text_Contents
, display_buffer
, TAG_DONE
);
393 static const char *localize_display(struct CalculatorData
*data
)
395 static char buffer
[EDIT_BUFFER_SIZE
];
396 memset(buffer
, 0, MAX_DIGITS
+ 1);
397 memcpy(buffer
, data
->edit_buffer
, data
->num_digits
);
398 localize_buffer(buffer
, data
->decimal_point
, 0, MAX_DIGITS
+ 1);
402 static void toggle_sign(struct CalculatorData
*data
)
404 BOOL sign_found
= FALSE
;
406 if (data
->state
== STATE_LEFTVAL
) data
->lvalue
= -data
->lvalue
;
407 else if (data
->state
== STATE_RIGHTVAL
) data
->rvalue
= -data
->rvalue
;
409 for (i
= 0; i
< EDIT_BUFFER_SIZE
; i
++)
411 if (data
->edit_buffer
[i
] == '-')
420 /* eliminate the sign by shifting to the left */
422 memmove(data
->edit_buffer
, data
->edit_buffer
+ 1, data
->num_digits
);
423 data
->edit_buffer
[data
->num_digits
] = 0;
427 /* add sign by shifting to the right and inserting - */
428 memmove(data
->edit_buffer
+ 1, data
->edit_buffer
, data
->num_digits
);
430 data
->edit_buffer
[0] = '-';
431 data
->edit_buffer
[data
->num_digits
] = 0;
437 * This method implements the main logic of the calculator by performing transitions
438 * of a state machine.
440 IPTR
mAddCalcKey(struct IClass
*cl
, Object
*obj
, struct MUIMP_CalcKey
*msg
)
442 struct CalculatorData
*data
= INST_DATA(cl
, obj
);
443 if (msg
->btype
<= BTYPE_9
&& data
->num_digits
< MAX_DIGITS
)
445 if (data
->state
== STATE_OP
)
447 data
->state
= STATE_RIGHTVAL
;
448 clear_edit_buffer(data
);
450 if (data
->state
== STATE_EQU
)
452 data
->state
= STATE_LEFTVAL
;
453 clear_edit_buffer(data
);
455 char digit
= '0' + msg
->btype
;
456 data
->edit_buffer
[data
->num_digits
++] = digit
;
460 else if (msg
->btype
== BTYPE_COMMA
&& can_insert_comma(data
))
462 data
->edit_buffer
[data
->num_digits
++] = '.';
465 else if (is_operator(msg
->btype
))
467 if (data
->state
== STATE_LEFTVAL
|| data
->state
== STATE_EQU
)
469 data
->lvalue
= strtod(data
->edit_buffer
, NULL
);
470 data
->state
= STATE_OP
;
471 data
->op
= msg
->btype
;
472 if (data
->tape
) DoMethod(data
->tape
, TAPEM_PRINT_LVAL
, localize_display(data
));
475 else if (msg
->btype
== BTYPE_EQU
)
477 if (data
->tape
) DoMethod(data
->tape
, TAPEM_PRINT_RVAL
, op2char(data
->op
), localize_display(data
));
479 data
->rvalue
= strtod(data
->edit_buffer
, NULL
);
480 data
->state
= STATE_EQU
;
481 data
->lvalue
= eval_result(data
->lvalue
, data
->rvalue
, data
->op
);
482 snprintf(data
->edit_buffer
, MAX_DIGITS
, "%f", data
->lvalue
);
483 /* note that there is no strnlen() in AROS !!! */
484 data
->num_digits
= strlen(data
->edit_buffer
);
487 if (data
->tape
) DoMethod(data
->tape
, TAPEM_PRINT_RESULT
, localize_display(data
));
489 else if (msg
->btype
== BTYPE_CA
)
493 data
->op
= BTYPE_ADD
;
494 data
->state
= STATE_LEFTVAL
;
495 clear_edit_buffer(data
);
498 if (data
->tape
) DoMethod(data
->tape
, TAPEM_NEWLINE
);
500 else if (msg
->btype
== BTYPE_CE
&&
501 (data
->state
== STATE_LEFTVAL
|| data
->state
== STATE_RIGHTVAL
))
503 clear_edit_buffer(data
);
507 else if (msg
->btype
== BTYPE_SIGN
&& data
->state
!= STATE_OP
)
511 else if (msg
->btype
== BTYPE_BS
&&
512 (data
->state
== STATE_LEFTVAL
|| data
->state
== STATE_RIGHTVAL
) &&
513 data
->num_digits
> 0)
515 data
->edit_buffer
[--data
->num_digits
] = 0;
521 IPTR
CalculatorDispatcher(struct IClass
*cl
, Object
*obj
, Msg msg
)
523 switch (msg
->MethodID
)
525 case OM_NEW
: return mNewCalc(cl
, obj
, (APTR
) msg
);
526 case OM_DISPOSE
: return mDisposeCalc(cl
, obj
, (APTR
) msg
);
527 case OM_ADD_KEY
: return (IPTR
) mAddCalcKey(cl
, obj
, (struct MUIMP_CalcKey
*) msg
);
529 return DoSuperMethodA(cl
, obj
, msg
);
532 static Class
*make_calculator_class(void)
535 cl
= MakeClass(NULL
, ROOTCLASS
, NULL
, sizeof(struct CalculatorData
), 0);
536 if (cl
) cl
->cl_Dispatcher
.h_Entry
= CalculatorDispatcher
;
540 /**********************************************************************
542 **********************************************************************/
544 static void cleanup(char *msg
)
549 fprintf(stderr
, "Calculator: %s\n", msg
);
559 static void dos_error(void)
561 static char tempstring
[256];
562 Fault(IoErr(), 0, tempstring
, 255);
566 static char retrieve_decimal_point(void)
571 if ((loc
= OpenLocale(0)))
573 result
= loc
->loc_DecimalPoint
[0];
579 static void get_arguments(void)
581 struct RDArgs
*rdargs
;
585 for (i
= 0; i
< NUM_ARGS
; i
++) args
[i
] = (IPTR
) NULL
;
587 if (!(rdargs
= ReadArgs(ARG_TEMPLATE
, (IPTR
*) args
,0))) dos_error();
589 if (args
[ARG_PUBSCREEN
]) {
590 strncpy(pubscrname
, (const char *) args
[ARG_PUBSCREEN
], 255);
596 strncpy(tapename
, (const char *) args
[ARG_TAPE
], 255);
598 if (rdargs
) FreeArgs(rdargs
);
601 static void open_raw_tape_if_needed(Object
*window
, Object
*obj_tape
)
603 if (use_tape
&& !strlen(tapename
))
609 GetAttr(MUIA_Window_Window
, window
, (IPTR
*) &win
);
610 w
= win
->Width
* 5 / 4;
615 if (x
> (win
->WScreen
->Width
- (x
+ w
))) x
-= w
;
616 else x
+= win
->WScreen
->Width
;
618 snprintf(tapename
, 255, RAW_TAPE_NAME
, x
, y
, w
, h
, pubscrname
);
619 tapefh
= Open(tapename
, MODE_NEWFILE
);
620 SetAttrs(obj_tape
, TAPEA_FILEHANDLE
, tapefh
);
626 Class
*cl_calc
= NULL
, *cl_tape
= NULL
;
627 Object
*app
= NULL
, *window
= NULL
, *display
= NULL
, *button
[NUM_BUTTONS
],
628 *obj_calc
, *obj_tape
;
629 char decimal_point
, decimal_label
[2];
630 struct Screen
*pub_screen
= NULL
;
635 decimal_point
= retrieve_decimal_point();
636 snprintf(decimal_label
, 2, "%c", decimal_point
);
638 display
= TextObject
,
640 MUIA_Text_Contents
, INITIAL_DISPLAY
,
641 MUIA_ShortHelp
, "Display",
644 cl_tape
= make_tape_class();
646 if (use_tape
&& strlen(tapename
)) {
647 tapefh
= Open(tapename
, MODE_NEWFILE
);
651 obj_tape
= NewObject(cl_tape
, NULL
, TAPEA_FILEHANDLE
, tapefh
, TAG_DONE
);
655 obj_tape
= NewObject(cl_tape
, NULL
, TAG_DONE
);
658 cl_calc
= make_calculator_class();
659 obj_calc
= NewObject(cl_calc
, NULL
,
660 CALCA_DISPLAY
, display
,
661 CALCA_TAPE
, obj_tape
,
662 CALCA_DECIMAL_POINT
, decimal_point
,
665 for (i
= 0; i
< NUM_BUTTONS
; i
++)
667 button
[i
] = (i
!= DECIMAL_BUTTON_INDEX
) ?
668 SimpleButton(BUTTONS
[i
].label
) : SimpleButton(decimal_label
);
669 if (BUTTONS
[i
].shortcut
)
671 SetAttrs(button
[i
], MUIA_ControlChar
, BUTTONS
[i
].shortcut
, TAG_DONE
);
675 if (strlen(pubscrname
))
677 pub_screen
= LockPubScreen((CONST_STRPTR
) pubscrname
);
680 printf("Can't lock public screen '%s' -> fallback to Wanderer!\n", pubscrname
);
681 memset(pubscrname
, 0, 256);
682 strcpy(pubscrname
, "Workbench");
683 pub_screen
= LockPubScreen((CONST_STRPTR
) pubscrname
);
687 app
= ApplicationObject
,
688 MUIA_Application_Title
, "Calculator",
689 MUIA_Application_Version
, "1.4",
690 MUIA_Application_Copyright
, "©2007-2013, AROS Dev Team",
691 MUIA_Application_Author
, "AROS Team",
692 MUIA_Application_Description
, "Simple desktop calculator",
693 MUIA_Application_Base
, "calculator",
694 SubWindow
, window
= WindowObject
,
695 MUIA_Window_Title
, "Calculator",
696 MUIA_Window_ID
, MAKE_ID('C', 'A', 'L', 'C'),
697 MUIA_Window_AppWindow
, TRUE
,
698 MUIA_Window_Screen
, pub_screen
,
699 WindowContents
, VGroup
,
702 Child
, HGroup
, GroupSpacing(5), MUIA_Group_SameWidth
, TRUE
,
710 Child
, HGroup
, GroupSpacing(5), MUIA_Group_SameWidth
, TRUE
,
718 Child
, HGroup
, GroupSpacing(5), MUIA_Group_SameWidth
, TRUE
,
726 Child
, HGroup
, GroupSpacing(5), MUIA_Group_SameWidth
, TRUE
,
738 DoMethod(window
, MUIM_Notify
, MUIA_Window_CloseRequest
, TRUE
,
739 app
, 2, MUIM_Application_ReturnID
,
740 MUIV_Application_ReturnID_Quit
);
742 for (i
= 0; i
< NUM_BUTTONS
; i
++)
744 DoMethod(button
[i
], MUIM_Notify
, MUIA_Pressed
, FALSE
,
745 obj_calc
, 2, OM_ADD_KEY
, BUTTONS
[i
].btype
);
748 SetAttrs(window
, MUIA_Window_Open
, TRUE
, TAG_DONE
);
749 open_raw_tape_if_needed(window
, obj_tape
);
751 if (pub_screen
) UnlockPubScreen(0, pub_screen
);
757 id
= DoMethod(app
, MUIM_Application_NewInput
, (IPTR
) &sigs
);
761 case MUIV_Application_ReturnID_Quit
:
769 sigs
= Wait(sigs
| SIGBREAKF_CTRL_C
);
770 if (sigs
& SIGBREAKF_CTRL_C
) break;
774 set((APTR
) window
, MUIA_Window_Open
, FALSE
);
775 MUI_DisposeObject(app
);
777 DisposeObject(obj_calc
);
778 DisposeObject(obj_tape
);