Add to git.
[monolith.git] / examples / toy_calculator.c
1 /* Toy calculator from example 02 turned into a reusable widget.
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: toy_calculator.c,v 1.3 2002/11/07 10:49:01 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 #include "ml_widget.h"
34
35 #include "toy_calculator.h"
36
37 static void repaint (void *, ml_session, const char *, io_handle);
38
39 struct ml_widget_operations toy_calculator_ops =
40   {
41     repaint: repaint
42   };
43
44 struct toy_calculator
45 {
46   struct ml_widget_operations *ops;
47   pool pool;                    /* Pool for allocations. */
48   ml_text_label disp;           /* The display. */
49   char digits[16];              /* Display digits (only 10+1 used). */
50   double reg;                   /* Hidden register. */
51   int op;                       /* Operation: PLUS, MINUS, TIMES, DIVIDE. */
52   ml_box box;                   /* The top-level box. */
53 };
54
55 /* Lots of callback functions for each button. */
56 static void press_button_0 (ml_session, void *);
57 static void press_button_1 (ml_session, void *);
58 static void press_button_2 (ml_session, void *);
59 static void press_button_3 (ml_session, void *);
60 static void press_button_4 (ml_session, void *);
61
62 static void press_button_5 (ml_session, void *);
63 static void press_button_6 (ml_session, void *);
64 static void press_button_7 (ml_session, void *);
65 static void press_button_8 (ml_session, void *);
66 static void press_button_9 (ml_session, void *);
67
68 static void press_button_DOT (ml_session, void *);
69 static void press_button_EQUALS (ml_session, void *);
70 static void press_button_PLUS (ml_session, void *);
71 static void press_button_MINUS (ml_session, void *);
72 static void press_button_TIMES (ml_session, void *);
73 static void press_button_DIVIDE (ml_session, void *);
74 static void press_button_CLEAR (ml_session, void *);
75 static void press_button_AC (ml_session, void *);
76
77 /* These are used as indexes in the b[] array. */
78 #define DOT 10
79 #define EQUALS 11
80 #define PLUS 12
81 #define MINUS 13
82 #define TIMES 14
83 #define DIVIDE 15
84 #define CLEAR 16
85 #define AC 17
86
87 toy_calculator
88 new_toy_calculator (pool pool, ml_session session)
89 {
90   toy_calculator w;
91   ml_box box;
92   ml_table_layout tbl;
93   ml_button b[18];
94   ml_text_label disp;
95
96   w = pmalloc (pool, sizeof *w);
97   w->ops = &toy_calculator_ops;
98   w->pool = pool;
99   strcpy (w->digits, "0");
100   w->reg = 0;
101   w->op = 0;
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, w);
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, w);
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, w);
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, w);
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, w);
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, w);
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, w);
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, w);
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, w);
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, w);
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, w);
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, w);
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, w);
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, w);
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, w);
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, w);
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, w);
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, w);
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. */
203   ml_box_pack (box, tbl);
204
205   /* Save the display widget in the widget structure so that the
206    * callback functions can update it.
207    */
208   w->disp = disp;
209
210   /* Save the box, so we can repaint. */
211   w->box = box;
212
213   return w;
214 }
215
216 static void press_button_N (toy_calculator, int);
217
218 static void
219 press_button_0 (ml_session session, void *vw)
220 {
221   toy_calculator w = (toy_calculator) vw;
222
223   press_button_N (w, 0);
224 }
225
226 static void
227 press_button_1 (ml_session session, void *vw)
228 {
229   toy_calculator w = (toy_calculator) vw;
230
231   press_button_N (w, 1);
232 }
233
234 static void
235 press_button_2 (ml_session session, void *vw)
236 {
237   toy_calculator w = (toy_calculator) vw;
238
239   press_button_N (w, 2);
240 }
241
242 static void
243 press_button_3 (ml_session session, void *vw)
244 {
245   toy_calculator w = (toy_calculator) vw;
246
247   press_button_N (w, 3);
248 }
249
250 static void
251 press_button_4 (ml_session session, void *vw)
252 {
253   toy_calculator w = (toy_calculator) vw;
254
255   press_button_N (w, 4);
256 }
257
258 static void
259 press_button_5 (ml_session session, void *vw)
260 {
261   toy_calculator w = (toy_calculator) vw;
262
263   press_button_N (w, 5);
264 }
265
266 static void
267 press_button_6 (ml_session session, void *vw)
268 {
269   toy_calculator w = (toy_calculator) vw;
270
271   press_button_N (w, 6);
272 }
273
274 static void
275 press_button_7 (ml_session session, void *vw)
276 {
277   toy_calculator w = (toy_calculator) vw;
278
279   press_button_N (w, 7);
280 }
281
282 static void
283 press_button_8 (ml_session session, void *vw)
284 {
285   toy_calculator w = (toy_calculator) vw;
286
287   press_button_N (w, 8);
288 }
289
290 static void
291 press_button_9 (ml_session session, void *vw)
292 {
293   toy_calculator w = (toy_calculator) vw;
294
295   press_button_N (w, 9);
296 }
297
298 static void
299 press_button_N (toy_calculator w, int n)
300 {
301   int len;
302
303   if (strcmp (w->digits, "0") == 0)
304     w->digits[0] = '\0';
305
306   len = strlen (w->digits);
307
308   if ((strchr (w->digits, '.') && len < 11) || len < 10)
309     {
310       w->digits[len] = '0' + n;
311       w->digits[len+1] = '\0';
312       ml_widget_set_property (w->disp, "text", w->digits);
313     }
314 }
315
316 static void
317 press_button_DOT (ml_session session, void *vw)
318 {
319   toy_calculator w = (toy_calculator) vw;
320   int len = strlen (w->digits);
321
322   if (strchr (w->digits, '.') == 0 && len < 10)
323     {
324       strcat (w->digits, ".");
325       ml_widget_set_property (w->disp, "text", w->digits);
326     }
327 }
328
329 static void
330 press_button_EQUALS (ml_session session, void *vw)
331 {
332   toy_calculator w = (toy_calculator) vw;
333   double a;
334
335   if (w->op)
336     {
337       sscanf (w->digits, "%lf", &a);
338
339       if (w->op == PLUS)
340         w->reg += a;
341       else if (w->op == MINUS)
342         w->reg -= a;
343       else if (w->op == TIMES)
344         w->reg *= a;
345       else if (w->op == DIVIDE && a != 0)
346         w->reg /= a;
347
348       snprintf (w->digits, 10, "%g", w->reg);
349       ml_widget_set_property (w->disp, "text", w->digits);
350       w->op = 0;
351     }
352 }
353
354 static void
355 press_button_PLUS (ml_session session, void *vw)
356 {
357   toy_calculator w = (toy_calculator) vw;
358
359   /* Act like we just pressed the EQUALS key. */
360   press_button_EQUALS (session, vw);
361
362   sscanf (w->digits, "%lf", &w->reg);
363   strcpy (w->digits, "0");      /* But DON'T update the label yet. */
364   w->op = PLUS;
365 }
366
367 static void
368 press_button_MINUS (ml_session session, void *vw)
369 {
370   toy_calculator w = (toy_calculator) vw;
371
372   /* Act like we just pressed the EQUALS key. */
373   press_button_EQUALS (session, vw);
374
375   sscanf (w->digits, "%lf", &w->reg);
376   strcpy (w->digits, "0");      /* But DON'T update the label yet. */
377   w->op = MINUS;
378 }
379
380 static void
381 press_button_TIMES (ml_session session, void *vw)
382 {
383   toy_calculator w = (toy_calculator) vw;
384
385   /* Act like we just pressed the EQUALS key. */
386   press_button_EQUALS (session, vw);
387
388   sscanf (w->digits, "%lf", &w->reg);
389   strcpy (w->digits, "0");      /* But DON'T update the label yet. */
390   w->op = TIMES;
391 }
392
393 static void
394 press_button_DIVIDE (ml_session session, void *vw)
395 {
396   toy_calculator w = (toy_calculator) vw;
397
398   /* Act like we just pressed the EQUALS key. */
399   press_button_EQUALS (session, vw);
400
401   sscanf (w->digits, "%lf", &w->reg);
402   strcpy (w->digits, "0");      /* But DON'T update the label yet. */
403   w->op = DIVIDE;
404 }
405
406 static void
407 press_button_CLEAR (ml_session session, void *vw)
408 {
409   toy_calculator w = (toy_calculator) vw;
410
411   strcpy (w->digits, "0");
412   ml_widget_set_property (w->disp, "text", "0");
413 }
414
415 static void
416 press_button_AC (ml_session session, void *vw)
417 {
418   toy_calculator w = (toy_calculator) vw;
419
420   strcpy (w->digits, "0");
421   w->reg = 0;
422   ml_widget_set_property (w->disp, "text", "0");
423 }
424
425 static void
426 repaint (void *vw, ml_session session, const char *windowid, io_handle io)
427 {
428   toy_calculator w = (toy_calculator) vw;
429
430   ml_widget_repaint (w->box, session, windowid, io);
431 }