Add to git.
[monolith.git] / examples / 02_toy_calculator.c
1 /* A toy calculator
2  * - by Richard W.M. Jones <rich@annexia.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  * $Id: 02_toy_calculator.c,v 1.9 2002/11/07 10:49:00 rich Exp $
19  */
20
21 #include <string.h>
22
23 #include <pool.h>
24 #include <pstring.h>
25 #include <pthr_cgi.h>
26
27 #include "monolith.h"
28 #include "ml_window.h"
29 #include "ml_table_layout.h"
30 #include "ml_text_label.h"
31 #include "ml_box.h"
32 #include "ml_button.h"
33
34 /* Main entry point to the app. */
35 static void app_main (ml_session);
36
37 int
38 handle_request (rws_request rq)
39 {
40   return ml_entry_point (rq, app_main);
41 }
42
43 /* Private per-session data. */
44 struct data
45 {
46   ml_text_label disp;           /* The display. */
47   char digits[16];              /* Display digits (only 10+1 used). */
48   double reg;                   /* Hidden register. */
49   int op;                       /* Operation: PLUS, MINUS, TIMES, DIVIDE. */
50 };
51
52 /* Lots of callback functions for each button. */
53 static void press_button_0 (ml_session, void *);
54 static void press_button_1 (ml_session, void *);
55 static void press_button_2 (ml_session, void *);
56 static void press_button_3 (ml_session, void *);
57 static void press_button_4 (ml_session, void *);
58
59 static void press_button_5 (ml_session, void *);
60 static void press_button_6 (ml_session, void *);
61 static void press_button_7 (ml_session, void *);
62 static void press_button_8 (ml_session, void *);
63 static void press_button_9 (ml_session, void *);
64
65 static void press_button_DOT (ml_session, void *);
66 static void press_button_EQUALS (ml_session, void *);
67 static void press_button_PLUS (ml_session, void *);
68 static void press_button_MINUS (ml_session, void *);
69 static void press_button_TIMES (ml_session, void *);
70 static void press_button_DIVIDE (ml_session, void *);
71 static void press_button_CLEAR (ml_session, void *);
72 static void press_button_AC (ml_session, void *);
73
74 /* These are used as indexes in the b[] array. */
75 #define DOT 10
76 #define EQUALS 11
77 #define PLUS 12
78 #define MINUS 13
79 #define TIMES 14
80 #define DIVIDE 15
81 #define CLEAR 16
82 #define AC 17
83
84 static void
85 app_main (ml_session session)
86 {
87   pool pool = ml_session_pool (session);
88   struct data *data;
89   ml_window w;
90   ml_box box;
91   ml_table_layout tbl;
92   ml_button b[18];
93   ml_text_label disp;
94
95   /* Create the private, per-session data area and save it in the
96    * session object.
97    */
98   data = pmalloc (pool, sizeof *data);
99
100   /* Create the top-level window. */
101   w = new_ml_window (session, pool);
102
103   /* Create the box surrounding the calculator. */
104   box = new_ml_box (pool);
105
106   /* A table layout widget is used to arrange the buttons and the screen.
107    * There are 6 rows, each with 4 columns.
108    */
109   tbl = new_ml_table_layout (pool, 6, 4);
110
111   /* Create the numeric buttons. */
112   b[0] = new_ml_button (pool, "0");
113   ml_button_set_callback (b[0], press_button_0, session, data);
114   ml_widget_set_property (b[0], "button.style", "key");
115   b[1] = new_ml_button (pool, "1");
116   ml_button_set_callback (b[1], press_button_1, session, data);
117   ml_widget_set_property (b[1], "button.style", "key");
118   b[2] = new_ml_button (pool, "2");
119   ml_button_set_callback (b[2], press_button_2, session, data);
120   ml_widget_set_property (b[2], "button.style", "key");
121   b[3] = new_ml_button (pool, "3");
122   ml_button_set_callback (b[3], press_button_3, session, data);
123   ml_widget_set_property (b[3], "button.style", "key");
124   b[4] = new_ml_button (pool, "4");
125   ml_button_set_callback (b[4], press_button_4, session, data);
126   ml_widget_set_property (b[4], "button.style", "key");
127
128   b[5] = new_ml_button (pool, "5");
129   ml_button_set_callback (b[5], press_button_5, session, data);
130   ml_widget_set_property (b[5], "button.style", "key");
131   b[6] = new_ml_button (pool, "6");
132   ml_button_set_callback (b[6], press_button_6, session, data);
133   ml_widget_set_property (b[6], "button.style", "key");
134   b[7] = new_ml_button (pool, "7");
135   ml_button_set_callback (b[7], press_button_7, session, data);
136   ml_widget_set_property (b[7], "button.style", "key");
137   b[8] = new_ml_button (pool, "8");
138   ml_button_set_callback (b[8], press_button_8, session, data);
139   ml_widget_set_property (b[8], "button.style", "key");
140   b[9] = new_ml_button (pool, "9");
141   ml_button_set_callback (b[9], press_button_9, session, data);
142   ml_widget_set_property (b[9], "button.style", "key");
143
144   /* Create the other buttons. */
145   b[DOT] = new_ml_button (pool, ".");
146   ml_button_set_callback (b[DOT], press_button_DOT, session, data);
147   ml_widget_set_property (b[DOT], "button.style", "key");
148   b[EQUALS] = new_ml_button (pool, "=");
149   ml_button_set_callback (b[EQUALS], press_button_EQUALS, session, data);
150   ml_widget_set_property (b[EQUALS], "button.style", "key");
151   b[PLUS] = new_ml_button (pool, "+");
152   ml_button_set_callback (b[PLUS], press_button_PLUS, session, data);
153   ml_widget_set_property (b[PLUS], "button.style", "key");
154   b[MINUS] = new_ml_button (pool, "-");
155   ml_button_set_callback (b[MINUS], press_button_MINUS, session, data);
156   ml_widget_set_property (b[MINUS], "button.style", "key");
157   b[TIMES] = new_ml_button (pool, "x");
158   ml_button_set_callback (b[TIMES], press_button_TIMES, session, data);
159   ml_widget_set_property (b[TIMES], "button.style", "key");
160   b[DIVIDE] = new_ml_button (pool, "/");
161   ml_button_set_callback (b[DIVIDE], press_button_DIVIDE, session, data);
162   ml_widget_set_property (b[DIVIDE], "button.style", "key");
163   b[CLEAR] = new_ml_button (pool, "C");
164   ml_button_set_callback (b[CLEAR], press_button_CLEAR, session, data);
165   ml_widget_set_property (b[CLEAR], "button.style", "key");
166   b[AC] = new_ml_button (pool, "AC");
167   ml_button_set_callback (b[AC], press_button_AC, session, data);
168
169   /* Create the display. */
170   disp = new_ml_text_label (pool, "0");
171   ml_widget_set_property (disp, "font.weight", "bold");
172   ml_widget_set_property (disp, "font.size", "large");
173
174   /* Pack the buttons and display into the table layout widget. */
175   ml_table_layout_pack (tbl, disp, 0, 0);
176   ml_table_layout_set_colspan (tbl, 0, 0, 4);
177   ml_table_layout_set_align (tbl, 0, 0, "right");
178
179   ml_table_layout_pack (tbl, b[CLEAR], 1, 2);
180   ml_table_layout_pack (tbl, b[AC], 1, 3);
181
182   ml_table_layout_pack (tbl, b[7], 2, 0);
183   ml_table_layout_pack (tbl, b[8], 2, 1);
184   ml_table_layout_pack (tbl, b[9], 2, 2);
185   ml_table_layout_pack (tbl, b[DIVIDE], 2, 3);
186
187   ml_table_layout_pack (tbl, b[4], 3, 0);
188   ml_table_layout_pack (tbl, b[5], 3, 1);
189   ml_table_layout_pack (tbl, b[6], 3, 2);
190   ml_table_layout_pack (tbl, b[TIMES], 3, 3);
191
192   ml_table_layout_pack (tbl, b[1], 4, 0);
193   ml_table_layout_pack (tbl, b[2], 4, 1);
194   ml_table_layout_pack (tbl, b[3], 4, 2);
195   ml_table_layout_pack (tbl, b[MINUS], 4, 3);
196
197   ml_table_layout_pack (tbl, b[DOT], 5, 0);
198   ml_table_layout_pack (tbl, b[0], 5, 1);
199   ml_table_layout_pack (tbl, b[EQUALS], 5, 2);
200   ml_table_layout_pack (tbl, b[PLUS], 5, 3);
201
202   /* Pack the table into the box and the box into the window. */
203   ml_box_pack (box, tbl);
204   ml_window_pack (w, box);
205
206   /* Save the display widget in the per-session data structure so
207    * that the callback functions can update it.
208    */
209   data->disp = disp;
210   strcpy (data->digits, "0");
211   data->reg = 0;
212   data->op = 0;
213 }
214
215 static void press_button_N (ml_session, int, void *);
216
217 static void
218 press_button_0 (ml_session session, void *vp)
219 {
220   press_button_N (session, 0, vp);
221 }
222
223 static void
224 press_button_1 (ml_session session, void *vp)
225 {
226   press_button_N (session, 1, vp);
227 }
228
229 static void
230 press_button_2 (ml_session session, void *vp)
231 {
232   press_button_N (session, 2, vp);
233 }
234
235 static void
236 press_button_3 (ml_session session, void *vp)
237 {
238   press_button_N (session, 3, vp);
239 }
240
241 static void
242 press_button_4 (ml_session session, void *vp)
243 {
244   press_button_N (session, 4, vp);
245 }
246
247 static void
248 press_button_5 (ml_session session, void *vp)
249 {
250   press_button_N (session, 5, vp);
251 }
252
253 static void
254 press_button_6 (ml_session session, void *vp)
255 {
256   press_button_N (session, 6, vp);
257 }
258
259 static void
260 press_button_7 (ml_session session, void *vp)
261 {
262   press_button_N (session, 7, vp);
263 }
264
265 static void
266 press_button_8 (ml_session session, void *vp)
267 {
268   press_button_N (session, 8, vp);
269 }
270
271 static void
272 press_button_9 (ml_session session, void *vp)
273 {
274   press_button_N (session, 9, vp);
275 }
276
277 static void
278 press_button_N (ml_session session, int n, void *vp)
279 {
280   struct data *data = (struct data *) vp;
281   int len;
282
283   if (strcmp (data->digits, "0") == 0)
284     data->digits[0] = '\0';
285
286   len = strlen (data->digits);
287
288   if ((strchr (data->digits, '.') && len < 11) || len < 10)
289     {
290       data->digits[len] = '0' + n;
291       data->digits[len+1] = '\0';
292       ml_widget_set_property (data->disp, "text", data->digits);
293     }
294 }
295
296 static void
297 press_button_DOT (ml_session session, void *vp)
298 {
299   struct data *data = (struct data *) vp;
300   int len = strlen (data->digits);
301
302   if (strchr (data->digits, '.') == 0 && len < 10)
303     {
304       strcat (data->digits, ".");
305       ml_widget_set_property (data->disp, "text", data->digits);
306     }
307 }
308
309 static void
310 press_button_EQUALS (ml_session session, void *vp)
311 {
312   struct data *data = (struct data *) vp;
313   double a;
314
315   if (data->op)
316     {
317       sscanf (data->digits, "%lf", &a);
318
319       if (data->op == PLUS)
320         data->reg += a;
321       else if (data->op == MINUS)
322         data->reg -= a;
323       else if (data->op == TIMES)
324         data->reg *= a;
325       else if (data->op == DIVIDE && a != 0)
326         data->reg /= a;
327
328       snprintf (data->digits, 10, "%g", data->reg);
329       ml_widget_set_property (data->disp, "text", data->digits);
330       data->op = 0;
331     }
332 }
333
334 static void
335 press_button_PLUS (ml_session session, void *vp)
336 {
337   struct data *data = (struct data *) vp;
338
339   /* Act like we just pressed the EQUALS key. */
340   press_button_EQUALS (session, data);
341
342   sscanf (data->digits, "%lf", &data->reg);
343   strcpy (data->digits, "0");   /* But DON'T update the label yet. */
344   data->op = PLUS;
345 }
346
347 static void
348 press_button_MINUS (ml_session session, void *vp)
349 {
350   struct data *data = (struct data *) vp;
351
352   /* Act like we just pressed the EQUALS key. */
353   press_button_EQUALS (session, data);
354
355   sscanf (data->digits, "%lf", &data->reg);
356   strcpy (data->digits, "0");   /* But DON'T update the label yet. */
357   data->op = MINUS;
358 }
359
360 static void
361 press_button_TIMES (ml_session session, void *vp)
362 {
363   struct data *data = (struct data *) vp;
364
365   /* Act like we just pressed the EQUALS key. */
366   press_button_EQUALS (session, data);
367
368   sscanf (data->digits, "%lf", &data->reg);
369   strcpy (data->digits, "0");   /* But DON'T update the label yet. */
370   data->op = TIMES;
371 }
372
373 static void
374 press_button_DIVIDE (ml_session session, void *vp)
375 {
376   struct data *data = (struct data *) vp;
377
378   /* Act like we just pressed the EQUALS key. */
379   press_button_EQUALS (session, data);
380
381   sscanf (data->digits, "%lf", &data->reg);
382   strcpy (data->digits, "0");   /* But DON'T update the label yet. */
383   data->op = DIVIDE;
384 }
385
386 static void
387 press_button_CLEAR (ml_session session, void *vp)
388 {
389   struct data *data = (struct data *) vp;
390
391   strcpy (data->digits, "0");
392   ml_widget_set_property (data->disp, "text", "0");
393 }
394
395 static void
396 press_button_AC (ml_session session, void *vp)
397 {
398   struct data *data = (struct data *) vp;
399
400   strcpy (data->digits, "0");
401   data->reg = 0;
402   ml_widget_set_property (data->disp, "text", "0");
403 }