Add to git.
[monolith.git] / doc / index.html
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2 <html>
3   <head>
4     <title>monolith documentation index</title>
5     <style type="text/css"><!--
6       h1 {
7       text-align: center;
8       }
9       pre {
10       background-color: #eeeeff;
11       }
12       code {
13       color: green;
14       font-weight: bold;
15       }
16       span.box {
17       display: block;
18       width: auto;
19       border-style: solid;
20       border-color: black;
21       border-width: thin;
22       padding: 6px 6px 6px 6px;
23       margin: 3px 3px 3px 3px;
24       }
25       --></style>
26   </head>
27
28   <body bgcolor="#ffffff">
29     <h1>monolith documentation index</h1>
30
31     <h2>What is monolith?</h2>
32
33     <p>
34       <code>monolith</code> is an application framework which
35       generates web-based applications.  It differs from existing web
36       tools because it doesn't work in terms of "pages" and "CGI
37       scripts". Instead it allows you to build applications using
38       reusable widgets (buttons, labels, etc.)  which you arrange into
39       windows. This makes it much more like building a traditional
40       application in Windows/MFC, Java/JFC, Tcl/Tk, Motif, etc.
41     </p>
42
43     <p>
44       You can also use the basic widgets to build reusable
45       "super-widgets" which you can then embed in other applications,
46       give away or sell.  An example might be a discussion system
47       "widget" which can be placed anywhere in another application.
48     </p>
49
50     <p>
51       Of course, there are limitations in the web and browsers which
52       means that you can't do everything you might do in a normal
53       application. But you can do most things.
54     </p>
55
56     <p>
57       <code>monolith</code> programs and widgets are written in C or
58       C++ (actually I've never used C++ with <code>monolith</code>,
59       but you are welcome to try).
60     </p>
61
62     <h3>CGI vs. rws's shared object scripts vs. monolith
63     applications</h3>
64
65     <p>
66       (This section is from the <code>rws</code> documentation page).
67     </p>
68
69     <p>
70       Shared object scripts are the direct analogy to CGI scripts,
71       the only difference being that CGI scripts are usually written
72       in very high level languages like Perl and PHP, and shared
73       object scripts are loaded into the server process for efficiency.
74       (Perl CGI scripts can also be loaded into the Apache
75       server process using <code>mod_perl</code>, and this is done
76       for similar reasons of efficiency).
77     </p>
78
79     <p>
80       <code>monolith</code> programs are entire applications, the sort of
81       thing which normally would be written using dozens of
82       cooperating CGI scripts. In the case of <code>monolith</code>, however,
83       the entire application compiles down to a single <code>.so</code>
84       file which happens to be (you guessed it) a shared object script.
85     </p>
86
87     <p>
88       Imagine that you are going to write yet another web-based email
89       client. For some reason you want to write this in C (please
90       don't try this at home: I wrote one in Perl at my last job and
91       that was hard enough). Here are three possible approaches
92       using C and <code>rws</code>:
93     </p>
94
95     <ol>
96       <li>
97         <p>
98           Write forty or so shared object scripts. Each displays
99           a single frame of the application, one might generate
100           the frameset, a couple of dozen to implement specific
101           operations like emptying trash or moving a message between
102           folders.
103         </p>
104         <p>
105           This is very much the normal way of writing CGI-based
106           applications.
107         </p>
108       <li> Write a <code>monolith</code> application. This will probably be
109         in lots of C files, but will compile down and be linked
110         into a single <code>.so</code> file (eg. <code>email.so</code>)
111         which is dropped into the <code>so-bin</code> directory.
112       <li>
113         <p>
114           Write a <code>monolith</code> email super-widget. This is going
115           to exist in a shared library called
116           <code>/usr/lib/libmyemail.so</code>
117           with a corresponding header file defining the interface
118           called <code>myemail.h</code>.
119         </p>
120         <p>
121           Write a tiny <code>monolith</code> application which just instantiates
122           a window and an email widget, and embeds the email widget
123           in the window. This will compile into <code>email.so</code>
124           (it'll be very tiny) which is dropped into <code>so-bin</code>.
125         </p>
126         <p>
127           The advantage of this final approach is that you can
128           reuse the email widget in other places, or indeed sell
129           it to other <code>monolith</code> users.
130         </p>
131     </ol>
132
133     <p>
134       So <code>monolith</code> is good when you want to build applications
135       from widgets as you would if you were building a
136       Java/Swing, Windows MFC, gtk, Tcl/Tk graphical application.
137       It's also good if code re-use is important to you.
138       Shared object scripts are good when you are familiar with
139       CGI-based techniques to build websites.
140     </p>
141
142     <p>
143       Of course, the same <code>rws</code> server can serve
144       shared object scripts, multiple <code>monolith</code> applications,
145       flat files, and directory listings, all at the same time.
146     </p>
147
148     <h2>Compiling and installing monolith programs</h2>
149
150     <p>
151       <code>monolith</code> programs are C programs which compile into
152       <code>.so</code> files, called shared object scripts.
153       The <code>rws</code> webserver runs these directly.
154     </p>
155
156     <p>
157       To compile, probably the simplest thing to do is write a
158       Makefile which does:
159     </p>
160
161 <pre>
162 gcc -Wall -Werror -c prog.c -o prog.o
163 gcc -shared -Wl,-soname,prog.so prog.o \
164                 -lmonolithcore -lpthrlib -lc2 -lm -o prog.so
165 </pre>
166
167     <p>
168       The last step generates the actual binary. Copy this into
169       <code>rws</code>'s
170       <code>/so-bin</code> directory, and make sure it is mode 0755
171       (<code>-rwxr-xr-x</code>).
172     </p>
173
174     <p>
175       To create an <code>/so-bin</code> directory, add this to your
176       <code>rws</code> hosts file:
177     </p>
178
179 <pre>
180 alias /so-bin/
181         path:           /path/to/your/so-bin
182         exec so:        1
183 end alias
184 </pre>
185
186     <p>
187       Now test it out by going to
188       <code>http://your.webserver/so-bin/prog.so</code>
189     </p>
190
191     <p>
192       If it doesn't work, here is a checklist before you email me:
193     </p>
194
195     <ul>
196       <li> Make sure you have put the above alias section into
197         the correct host file.
198       <li> <code>exec so</code> option is set?
199       <li> Restarted <code>rwsd</code>?
200       <li> Directory is world readable, executable (mode 0755)?
201       <li> Program is world readable, executable (mode 0755)?
202       <li> Any unresolved symbols (<code>ldd -r script.so</code>), apart
203         from the <code>rws_request_*</code> symbols which will be resolved
204         when the library is loaded into <code>rws</code>?
205       <li> Check the contents of your error_log file to see
206         if any error messages were reported.
207     </ul>
208
209     <p>
210       I have quite successfully used <code>gdb</code> on a running
211       server to debug and diagnose problems in <code>monolith</code> programs.
212       However note that by default <code>gdb</code> may have trouble
213       loading the symbol table for the <code>monolithcore</code>
214       library and your program. Use the <code>sharedlibrary monolith;
215       sharedlibrary script.so</code> command to load symbols instead.
216     </p>
217
218     <h2>Tutorial</h2>
219
220     <p>
221       This tutorial begins with the basics of <code>monolith</code>
222       application design, and then goes through some of the examples
223       which you will find in the <code>examples/</code> directory
224       in the source distribution.
225     </p>
226
227     <h3>A simple "hello, world" program</h3>
228
229     <p>
230       The simple "hello, world" program is useful because it
231       lets us (a) make sure our development environment is <em>really</em>
232       working, and (b) sort out the boilerplate code that every
233       <code>monolith</code> application needs. You can find
234       the full program in the source distribution as
235       <code>doc/hello.c</code>.
236     </p>
237
238     <p>
239       Start by including some necessary headers:
240     </p>
241
242 <pre>
243 #include &lt;pool.h&gt;
244
245 #include &lt;monolith.h&gt;
246 #include &lt;ml_window.h&gt;
247 #include &lt;ml_text_label.h&gt;
248 </pre>
249
250     <p>
251       Then there is some standard boilerplate code that needs to
252       go in (once only) into every <code>monolith</code> application.
253       You don't need to worry about what it does: it's just
254       glue between <code>rws</code>'s shared object scripts and
255       the <code>monolith</code> core code:
256     </p>
257
258 <pre>
259 /*----- The following standard boilerplate code must appear -----*/
260
261 /* Main entry point to the app. */
262 static void app_main (ml_session);
263
264 int
265 handle_request (rws_request rq)
266 {
267   return ml_entry_point (rq, app_main);
268 }
269
270 /*----- End of standard boilerplate code -----*/
271 </pre>
272
273     <p>
274       Then the "hello, world" program itself:
275     </p>
276
277 <pre>
278 static void
279 app_main (ml_session session)
280 {
281   pool pool = ml_session_pool (session);
282   ml_window w;
283   ml_text_label text;
284
285   /* Create the top-level window. */
286   w = new_ml_window (session, pool);
287
288   /* Create the text widget. */
289   text = new_ml_text_label (pool, "Hello, World!");
290
291   /* Pack the text widget into the window. */
292   ml_window_pack (w, text);
293 }
294 </pre>
295
296     <p>
297       <code>app_main</code> is the entry point into the
298       application. It has one parameter, the opaque
299       <code>ml_session</code> object, which is explained below.  Every
300       <code>monolith</code> program needs a top-level window, so we
301       create this using <code>new_ml_window(3)</code>.
302       Now we need to put something into the window, otherwise
303       it'll appear completely blank. In this case we're
304       going to put a text label widget inside this window
305       containing the familiar greeting. We have to tell the
306       window that it contains the text widget (otherwise
307       they'd be just two completely separate variables),
308       so we call <code>ml_window_pack(3)</code> to place the
309       text label inside the window.
310     </p>
311
312     <p>
313       Now you should compile and run this example
314       (in <code>doc/hello.c</code>) using the instructions
315       above and verify that it runs.
316     </p>
317
318     <h3>Design: sessions, session pools, shared data, session data</h3>
319
320     <p>
321       The <code>app_main</code> function has just one argument
322       passed to it, the opaque <code>ml_session</code> object.
323       What is this used for?
324     </p>
325
326     <p>
327       Think of a normal GUI application written in C or C++ (or
328       another language if you like). A user starts up the
329       application. The application interacts exclusively
330       with that one user. Global variables in the application
331       belong entirely to that application and that user.
332       The application continues to be used until the user
333       closes it down. We will define this process of the user
334       starting up the application, using the application and
335      then finally closing it down, as a <dfn>session</dfn>.
336     </p>
337
338     <p>
339       Web-based applications are slightly different in that
340       the same code running in the same Unix process can be
341       used by many users at the same time. For the benefit
342       of programmers, <code>monolith</code> automatically
343       keeps all of these users' sessions separate for you,
344       maintaining a different opaque <code>ml_session</code> 
345       object for each session.
346     </p>
347
348     <p>
349       One implication of this is that <code>app_main</code> is
350       called with a different <code>ml_session</code> object
351       each time, because a session only starts once. (The same
352       <i>user</i> might come back later and use the same
353       <i>application</i>, but that would be a <em>different session</em>).
354     </p>
355
356     <p>
357       So a session is different from a user. A session is also
358       different from an HTTP request. Typically during a session
359       a user might fill in a few forms, press some buttons,
360       browse through tables and so on. Each of these operations
361       probably involves an HTTP request. So in <code>monolith</code>
362       HTTP requests are very short-lived (of the order of 1ms - 1s),
363       but sessions can be quite long (hours of activity).
364     </p>
365
366     <p>
367       <code>monolith</code> allocates a separate session pool
368       for each session, and most applications are expected
369       to allocate most of their data on this session pool.
370       Of course when a user finishes their session, the session
371       pool will be deleted. This normally results in all of the
372       widgets and stuff created during the session being nuked,
373       and this is generally a good thing.
374     </p>
375
376     <p>
377       C static variables (ie. globals and variables in functions
378       declared using <code>static</code>) are shared between
379       all sessions. This can be useful under some circumstances,
380       but if what you really want is <em>persistent</em> data,
381       then you are far better off using a database of some sort
382       at the back-end. This is because static variables will
383       obviously be trashed if the <code>monolith</code> application
384       is unloaded or the webserver crashes.
385     </p>
386
387     <p>
388       <code>c2lib</code>'s <code>global_pool</code> is also
389       shared between all sessions (but don't use it directly:
390       create a subpool in your <code>_init</code> function
391       and delete the subpool in your <code>_fini</code> function).
392     </p>
393
394     <p>
395       If C static variables are shared, how do we keep a separate
396       set of variables for each session? Generally the easiest
397       way to do this is to allocate a per-session structure
398       once in <code>app_main</code> and pass it around. This
399       is called the <dfn>session data structure</dfn>. We'll
400       see this being done in example 01 below.
401     </p>
402
403     <h3>Design (for CGI programmers): what are widgets?</h3>
404
405     <p>
406       A short aside: what are widgets? If you have any experience
407       of using a traditional GUI library or GUI-builder tool, like
408       Tcl/Tk, gtk, Windows MFC, Motif, Java Swing, etc., then you'll
409       probably already know what a widget (or "control" in MS-speak) is,
410       so skip to the next section. This section is for people
411       coming from a non-graphical or purely CGI background.
412     </p>
413
414     <p>
415       In traditional GUI environments, applications are not
416       built up using pages, forms, CGI scripts and so on,
417       but are instead built up using small reusable objects
418       called widgets. A typical basic widget might be a
419       push button, a label, or an image. There are also
420       compound widgets which store other widgets inside
421       themselves. In <code>monolith</code> for example,
422       a table layout can be used to arrange other widgets
423       into a table. A table layout is itself a widget, and
424       you can use a table layout (populated with widgets inside)
425       any place you would use a basic widget. This is important
426       because complex layouts are often constructed from several
427       layers of compound and basic widgets (buttons and labels inside
428       table layouts inside other table layouts inside windows, etc.)
429     </p>
430
431     <p>
432       Here is an example form constructed using nested widgets:
433     </p>
434
435     <span class="box">
436       <i> Window </i>
437       <span class="box">
438         <i> Form </i>
439         <span class="box">
440           <i> Table layout </i>
441     <table border="1" width="100%">
442         <tr>
443           <td colspan="2"> <i>Label</i> </td>
444         </tr>
445         <tr>
446           <td> <i>Label</i> </td>
447           <td> <i>Form input</i> </td>
448         </tr>
449         <tr>
450           <td> <i>Label</i> </td>
451           <td> <i>Form input</i> </td>
452         </tr>
453         <tr>
454           <td> <i>Empty</i> </td>
455           <td> <i>Form submit</i> </td>
456         </tr>
457     </table>
458   </span>
459   </span>
460   </span>
461
462     <h3>Example 01: Label and button</h3>
463
464     <p>
465       The first example, <code>examples/01_label_and_button.c</code>,
466       is very simple. It displays a label and a button. Clicking
467       on the button increments the number on the label. Try this
468       now. Also try running it from two different browsers and
469       machines. Notice how the label starts counting up from 0
470       independently on each machine (demonstrating that each session
471       is really independent because this demo uses a session data
472       structure).
473     </p>
474
475     <p>
476       As before we begin by including some necessary headers and
477       the same boilerplate code as in our "hello, world" example
478       above. I won't repeat that here, because it's identical.
479       Then we declare our session data structure and a few private
480       functions:
481     </p>
482
483 <pre>
484 struct data
485 {
486   ml_label lb;                  /* Label. */
487   int count;                    /* Count of number of button presses. */
488 };
489
490 static void increment (ml_session, void *);
491 static void update_label (pool pool, ml_label lb, int count);
492 </pre>
493
494     <p>
495       We keep (a pointer to) the label widget and the count of
496       button presses in our session data structure. In theory
497       we could keep more here, but in fact it's not necessary. These
498       are the only two variables that we need to make "global"
499       to the session, because these are the only two variables
500       which our callback function will need when it comes to
501       update the label. Our callback function is going to be
502       called when the user clicks on the button, as we'll see
503       in a moment.
504     </p>
505
506     <p>
507       The main entry point to our application is called
508       <code>app_main</code>. It's called at the beginning
509       of the session:
510     </p>
511
512 <pre>
513 static void
514 app_main (ml_session session)
515 {
516   pool pool = ml_session_pool (session);
517   struct data *data;
518   ml_window w;
519   ml_flow_layout lay;
520   ml_label lb;
521   ml_button b;
522 </pre>
523
524     <p>
525       Notice that we use <code>ml_session_pool(3)</code> to get the
526       current session pool, where we are going to make all of our
527       allocations.
528     </p>
529
530     <p>
531       <code>data</code> will point to our session data structure, but
532       we have to allocate and initialise it first:
533     </p>
534
535 <pre>
536   /* Create the private, per-session data area and save it in the
537    * session object.
538    */
539   data = pmalloc (pool, sizeof *data);
540   data-&gt;count = 0;
541 </pre>
542
543     <p>
544       Next we create the window, label and button. We're going to
545       pack the label and button into a flow layout which is the
546       simplest sort of compound widget: it just displays the widgets
547       inside itself one after another.
548     </p>
549
550 <pre>
551   /* Create the top-level window. */
552   w = new_ml_window (session, pool);
553
554   /* Create the flow layout widget which will be packed into the window. */
555   lay = new_ml_flow_layout (pool);
556
557   /* Create the label and button. */
558   data-&gt;lb = lb = new_ml_label (pool, 0);
559   update_label (pool, data-&gt;lb, 0);
560
561   b = new_ml_button (pool, "Push me!");
562   ml_button_set_callback (b, increment, session, data);
563
564   /* Pack the label and button into the flow layout widget. */
565   ml_flow_layout_pack (lay, lb);
566   ml_flow_layout_pack (lay, b);
567
568   /* Pack the flow layout widget into the window. */
569   ml_window_pack (w, lay);
570 }
571 </pre>
572
573     <p>
574       Notice the call to <code>ml_button_set_callback</code>. When the
575       button is pressed, the <code>increment</code> function will
576       be called like this: <code>increment (session, data)</code>.
577       (Recall that <code>data</code> is our session data pointer).
578     </p>
579
580     <p>
581       This is the definition of <code>increment</code>:
582     </p>
583
584 <pre>
585 static void
586 increment (ml_session session, void *vp)
587 {
588   struct data *data = (struct data *) vp;
589
590   update_label (ml_session_pool (session), data-&gt;lb, ++data-&gt;count);
591 }
592 </pre>
593
594     <p>
595       It increments <code>data-&gt;count</code> and calls
596       <code>update_label</code> which is the function which
597       actually changes the message on the label.
598     </p>
599
600     <p>
601       <code>update_label</code> is defined as:
602     </p>
603
604 <pre>
605 static void
606 update_label (pool pool, ml_label lb, int count)
607 {
608   ml_label_set_text (lb,
609                      psprintf (pool,
610                                "Button pressed: &lt;b&gt;%d&lt;/b&gt;&lt;br&gt;",
611                                count));
612 }
613 </pre>
614
615     <p>
616       (If you are unfamiliar with the function <code>psprintf</code>
617       then you should read the <code>c2lib</code> documentation).
618     </p>
619
620     <p>
621       That's the end of our first significant <code>monolith</code>
622       application! At around 31 lines of code, it's considerably smaller
623       than the equivalent CGI script.
624     </p>
625
626     <h3>Design: Applications and widgets</h3>
627
628     <p>
629       This section talks about one of the more fundamental
630       design considerations you need to think about when first
631       designing a new <code>monolith</code> application.
632       Namely when to build application code and when to
633       build reusable widgets.
634     </p>
635
636     <p>
637       The label and button example above is a complete
638       <code>monolith</code> application. What happens
639       however if we needed to embed this label and button
640       combo in another program (not a very likely scenario,
641       I'll admit, but let's imagine that you've developed
642       a calendar program or something else quite substantial).
643     </p>
644
645     <p>
646       The answer is to turn your application into a reusable
647       widget. Widgets are often composed of many other
648       more fundamental widgets, and in this case it is possible
649       to turn the label and button combo into a full-blown
650       widget. This widget could be used just like any of the
651       core widgets which <code>monolith</code> provides.
652     </p>
653
654     <p>
655       Turning an application into a widget isn't too hard, depending
656       on the complexity of the application itself, but it's better
657       when designing the application if you first of all work out
658       whether the application as a whole -- or parts of the
659       application -- can be designed as reusable widgets.
660     </p>
661
662     <p>
663       If you were designing a calendar program in <code>monolith</code>
664       then you might decompose the design like this:
665     </p>
666
667     <table border="1">
668         <tr>
669           <th> Component </th>
670           <th> Specification </th>
671           <th> Can be used as a widget? </th>
672         </tr>
673         <tr>
674           <td> Whole application </td>
675           <td> Calendar: A tool for storing and tracking daily
676             events, providing appointments, backed up in a database </td>
677           <td> Yes. By writing the whole calendar as a reusable
678             widget, we can include the calendar widget in an Outlook-style
679             personal information manager (PIM). </td>
680         </tr>
681         <tr>
682           <td> New event form </td>
683           <td> Form which appears when the user adds a new event. </td>
684           <td> No. Quite specific to this calendar, so reuse doesn't
685             make much sense. </td>
686         </tr>
687         <tr>
688           <td> Date selector </td>
689           <td> Form input which allows the user to choose a date
690             and automatically verifies it. </td>
691           <td> Yes. This is applicable on many different forms in
692             other applications. </td>
693         </tr>
694     </table>
695
696     <h3>Example 03: Toy calculators</h3>
697
698     <p>
699       Example 03 will demonstrate how to write a simple reusable
700       widget. In fact the reusable widget that we're going to write
701       is the same as the application in example 02 (not covered
702       here, but supplied with the source in the <code>examples/</code>
703       directory). So if you want you can study the process of
704       converting a whole application into a reusable widget.
705     </p>
706
707     <p>
708       The source code for example 03 is divided into three
709       files:
710     </p>
711
712     <ul>
713       <li> <code>03_many_toy_calculators.c</code> <br>
714         The application. This just instantiates four widgets
715         and displays them.
716       <li> <code>toy_calculator.h</code> <br>
717         The widget's header file. Every widget should have a
718         header file defining the external interface which
719         callers are allowed to use, and defining also the
720         opaque widget type (in this case, <code>toy_calculator</code>).
721       <li> <code>toy_calculator.c</code> <br>
722         The actual code which implements the widget.
723     </ul>
724
725     <p>
726       It's helpful at this point if you run the example. You
727       should see four calculators. Try doing some sums. Notice
728       how each calculator acts completely independently of
729       the others.
730     </p>
731
732     <p>
733       Let's start with the application code. This is very simple. It
734       just creates four toy_calculator objects and populates a
735       table layout widget with them:
736     </p>
737
738 <pre>
739 static void
740 app_main (ml_session session)
741 {
742   pool pool = ml_session_pool (session);
743   ml_window w;
744   ml_table_layout tbl;
745   toy_calculator calcs[4];
746
747   /* Create the top-level window. */
748   w = new_ml_window (session, pool);
749
750   /* Create a table layout widget to arrange the calculators. */
751   tbl = new_ml_table_layout (pool, 2, 2);
752
753   /* Create the calculators and pack them into the table layout. */
754   calcs[0] = new_toy_calculator (pool, session);
755   ml_table_layout_pack (tbl, calcs[0], 0, 0);
756   calcs[1] = new_toy_calculator (pool, session);
757   ml_table_layout_pack (tbl, calcs[1], 0, 1);
758   calcs[2] = new_toy_calculator (pool, session);
759   ml_table_layout_pack (tbl, calcs[2], 1, 0);
760   calcs[3] = new_toy_calculator (pool, session);
761   ml_table_layout_pack (tbl, calcs[3], 1, 1);
762
763   /* Pack the table into the window. */
764   ml_window_pack (w, tbl);
765 }
766 </pre>
767
768     <p>
769       The header file <code>toy_calculator.h</code> defines the
770       interface. It's very simple, because there's only one
771       thing you can do with a <code>toy_calculator</code> at
772       the moment, and that's create one by using the
773       <code>new_toy_calculator</code> function. Notice the
774       <a href="http://www.annexia.org/freeware/c2lib/">cdoc</a>
775       documentation in the comments.
776     </p>
777
778 <pre>
779 #ifndef TOY_CALCULATOR_H
780 #define TOY_CALCULATOR_H
781
782 #include &lt;pool.h&gt;
783 #include &lt;monolith.h&gt;
784
785 struct toy_calculator;
786 typedef struct toy_calculator *toy_calculator;
787
788 /* Function: new_toy_calculator - toy calculator widget
789  *
790  * @code{new_toy_calculator} creates a new reusable toy calculator
791  * widget.
792  */
793 extern toy_calculator new_toy_calculator (pool, ml_session);
794
795 #endif /* TOY_CALCULATOR_H */
796 </pre>
797
798     <p>
799       The actual implementation of the <code>toy_calculator</code>
800       widget is complicated, so we'll take it step by step here.
801       However remember that if all you want to do is to <em>use</em>
802       a <code>toy_calculator</code>, then you needn't worry about
803       the implementation at all. You only need to look at the
804       header file and use it just like you would any other widget.
805     </p>
806
807     <p>
808       <code>toy_calculator.c</code> begins by including many
809       header files.
810     </p>
811
812 <pre>
813 #include &lt;string.h&gt;
814
815 #include &lt;pool.h&gt;
816 #include &lt;pstring.h&gt;
817 #include &lt;pthr_cgi.h&gt;
818
819 #include &lt;monolith.h&gt;
820 #include &lt;ml_window.h&gt;
821 #include &lt;ml_table_layout.h&gt;
822 #include &lt;ml_text_label.h&gt;
823 #include &lt;ml_box.h&gt;
824 #include &lt;ml_button.h&gt;
825 #include &lt;ml_widget.h&gt;
826
827 #include "toy_calculator.h"
828 </pre>
829
830     <p>
831       Every widget has an associated structure, which is where it
832       stores all its private data. For callers, the widget structure
833       is opaque (notice how it is defined in the
834       <code>toy_calculator.h</code> header file above). The
835       <code>toy_calculator</code> object we've been passing around
836       above is in fact a pointer, typedef'd to <code>struct
837       toy_calculator *</code>. (This is a common coding convention in
838       <code>c2lib</code> and <code>pthrlib</code>). In the actual
839       implementation, obviously we need to see the private members.
840     </p>
841
842     <p>
843       Moreover, every widget structure <strong>must</strong> begin
844       with a pointer to <code>struct ml_widget_operations</code>
845       because the <code>toy_calculator</code> object "inherits"
846       from the abstract base class <code>ml_widget</code>.
847     </p>
848
849     <p>
850       Here is the definition of <code>struct toy_calculator</code>:
851     </p>
852
853 <pre>
854 static void repaint (void *, ml_session, const char *, io_handle);
855
856 struct ml_widget_operations toy_calculator_ops =
857   {
858     repaint: repaint
859   };
860
861 struct toy_calculator
862 {
863   struct ml_widget_operations *ops;
864   pool pool;                    /* Pool for allocations. */
865   ml_text_label disp;           /* The display. */
866   char digits[16];              /* Display digits (only 10+1 used). */
867   double reg;                   /* Hidden register. */
868   int op;                       /* Operation: PLUS, MINUS, TIMES, DIVIDE. */
869   ml_box box;                   /* The top-level box. */
870 };
871 </pre>
872
873     <p>
874       <code>repaint</code> is going to be the function which
875       actually draws our widget, but we'll see that a little bit
876       later.
877     </p>
878
879     <p>
880       The most important function we need to define is
881       <code>new_toy_calculator</code> which creates new
882       <code>toy_calculator</code> widgets. I won't show this
883       function in full because it's quite long (it needs to
884       create one <code>ml_button</code> object for every
885       button on the calculator, and there are 18 of them in all!).
886       But here's the important outline.
887     </p>
888
889     <p>
890       We start by allocating and initialising a new <code>struct
891       toy_calculator</code>.  A pointer to this is stored in <code>w</code>.
892     </p>
893
894 <pre>
895 toy_calculator
896 new_toy_calculator (pool pool, ml_session session)
897 {
898   toy_calculator w;
899   ml_box box;
900   ml_table_layout tbl;
901   ml_button b[18];
902   ml_text_label disp;
903
904   w = pmalloc (pool, sizeof *w);
905   w-&gt;ops = &amp;toy_calculator_ops;
906   w-&gt;pool = pool;
907   strcpy (w-&gt;digits, "0");
908   w-&gt;reg = 0;
909   w-&gt;op = 0;
910 </pre>
911
912     <p>
913       Then some code creates the actual widgets contained inside
914       the calculator, the top level being a box widget:
915     </p>
916
917 <pre>
918   /* Create the box surrounding the calculator. */
919   box = new_ml_box (pool);
920
921   /* A table layout widget is used to arrange the buttons and the screen.
922    * There are 6 rows, each with 4 columns.
923    */
924   tbl = new_ml_table_layout (pool, 6, 4);
925
926   /* Create the numeric buttons. */
927   b[0] = new_ml_button (pool, "0");
928   ml_button_set_callback (b[0], press_button_0, session, w);
929   ml_button_set_key (b[0], 1);
930          :      :     :
931          :      :     :
932          :      :     :
933 </pre>
934
935     <p>
936       Finally we pack everything up and return the widget
937       pointer (<code>w</code>):
938     </p>
939
940 <pre>
941          :      :     :
942          :      :     :
943          :      :     :
944   /* Pack the table into the box. */
945   ml_box_pack (box, tbl);
946
947   /* Save the display widget in the widget structure so that the
948    * callback functions can update it.
949    */
950   w-&gt;disp = disp;
951
952   /* Save the box, so we can repaint. */
953   w-&gt;box = box;
954
955   return w;
956 }
957 </pre>
958
959     <p>
960       When a button is pressed, one of the appropriate callback
961       functions is called. Because there are 18 buttons, there are
962       18 separate callback functions, but we only reproduce a few
963       here. Notice how the <code>toy_calculator</code> pointer
964       is passed as the second argument (the <code>void *</code>),
965       so we need to cast this back before using it:
966     </p>
967
968 <pre>
969 static void press_button_N (toy_calculator, int);
970
971 static void
972 press_button_0 (ml_session session, void *vw)
973 {
974   toy_calculator w = (toy_calculator) vw;
975
976   press_button_N (w, 0);
977 }
978
979          :      :     :
980          :      :     :
981          :      :     :
982
983 static void
984 press_button_N (toy_calculator w, int n)
985 {
986   int len;
987
988   if (strcmp (w-&gt;digits, "0") == 0)
989     w-&gt;digits[0] = '\0';
990
991   len = strlen (w-&gt;digits);
992
993   if ((strchr (w-&gt;digits, '.') &amp;&amp; len &lt; 11) || len &lt; 10)
994     {
995       w-&gt;digits[len] = '0' + n;
996       w-&gt;digits[len+1] = '\0';
997       ml_text_label_set_text (w-&gt;disp, w-&gt;digits);
998     }
999 }
1000 </pre>
1001
1002     <p>
1003       Here's the callback function which runs when the [AC] button
1004       is pressed:
1005     </p>
1006
1007 <pre>
1008 static void
1009 press_button_AC (ml_session session, void *vw)
1010 {
1011   toy_calculator w = (toy_calculator) vw;
1012
1013   strcpy (w-&gt;digits, "0");
1014   w-&gt;reg = 0;
1015   ml_text_label_set_text (w-&gt;disp, "0");
1016 }
1017 </pre>
1018
1019     <p>
1020       Finally our widget must know how to repaint (redisplay) itself.
1021       <code>monolith</code> will call the repaint function at
1022       the appropriate moment, and it must generate the HTML corresponding
1023       to the widget. In the case of this widget, it's a compound
1024       widget built up entirely out of core <code>monolith</code>
1025       widgets. The top-level widget inside the calculator is the
1026       box (<code>w-&gt;box</code>), so we just call the repaint
1027       function for that:
1028     </p>
1029
1030 <pre>
1031 static void
1032 repaint (void *vw, ml_session session, const char *windowid, io_handle io)
1033 {
1034   toy_calculator w = (toy_calculator) vw;
1035
1036   ml_widget_repaint (w-&gt;box, session, windowid, io);
1037 }
1038 </pre>
1039
1040     <p>
1041       That's all. Our reusable toy calculator is now complete.
1042     </p>
1043
1044     <h3>Example 06: Forms</h3>
1045
1046     <p>
1047       Example 06 shows the various input controls available
1048       when using forms. The example is straightforward, albeit
1049       rather long because of the number of different controls
1050       demonstrated. We begin with an unusually long list of
1051       includes:
1052     </p>
1053
1054 <pre>
1055 #include &lt;string.h&gt;
1056
1057 #include &lt;pool.h&gt;
1058 #include &lt;pstring.h&gt;
1059 #include &lt;pthr_cgi.h&gt;
1060
1061 #include &lt;monolith.h&gt;
1062 #include &lt;ml_window.h&gt;
1063 #include &lt;ml_text_label.h&gt;
1064 #include &lt;ml_button.h&gt;
1065 #include &lt;ml_table_layout.h&gt;
1066 #include &lt;ml_flow_layout.h&gt;
1067 #include &lt;ml_form.h&gt;
1068 #include &lt;ml_form_submit.h&gt;
1069 #include &lt;ml_form_text.h&gt;
1070 #include &lt;ml_form_textarea.h&gt;
1071 #include &lt;ml_form_password.h&gt;
1072 #include &lt;ml_form_select.h&gt;
1073 #include &lt;ml_form_radio_group.h&gt;
1074 #include &lt;ml_form_radio.h&gt;
1075 #include &lt;ml_form_checkbox.h&gt;
1076 #include &lt;ml_form_textarea.h&gt;
1077 </pre>
1078
1079     <p>
1080       The private session data structure contains pointers to the
1081       form input widgets so that our callback functions are able
1082       to read their contents:
1083     </p>
1084
1085 <pre>
1086 /* Private per-session data. */
1087 struct data
1088 {
1089   ml_window win;
1090
1091   /* The form input fields themselves. */
1092   ml_form_text familyname, givenname;
1093   ml_form_password password;
1094   ml_form_select dd, mm, yyyy;  /* Date of birth. */
1095   ml_form_radio_group gender;
1096   ml_form_radio m, f;           /* Gender. */
1097   ml_form_checkbox eating, drinking, sleeping; /* Interests */
1098   /*ml_form_file photo;            File upload, not yet implemented. */
1099   ml_form_select dept;
1100   ml_form_textarea comments;
1101 };
1102 </pre>
1103
1104     <p>
1105       <code>app_main</code> allocates the session data structure
1106       and calls <code>create_form</code> which actually creates
1107       the initial form:
1108     </p>
1109
1110 <pre>
1111 static void
1112 app_main (ml_session session)
1113 {
1114   pool pool = ml_session_pool (session);
1115   struct data *data;
1116
1117   /* Create the private, per-session data area and save it in the
1118    * session object.
1119    */
1120   data = pmalloc (pool, sizeof *data);
1121
1122   /* Create the top-level window. */
1123   data-&gt;win = new_ml_window (session, pool);
1124
1125   create_form (session, data);
1126 }
1127 </pre>
1128
1129     <p>
1130       <code>create_form</code> is quite a long, but not very
1131       complex function. It creates an input control of each
1132       type. Notice first the visual structure of the form:
1133     </p>
1134
1135     <span class="box">
1136       <i> Window </i>
1137       <span class="box">
1138         <i> Form </i>
1139         <span class="box">
1140           <i> Table layout </i>
1141     <table border="1" width="100%">
1142         <tr>
1143           <td> <i>Label</i> </td>
1144           <td> <i>Form input</i> </td>
1145         </tr>
1146         <tr>
1147           <td> <i>Label</i> </td>
1148           <td> <i>Form input</i> </td>
1149         </tr>
1150         <tr>
1151           <td colspan="2"> <i>etc.</i> </td>
1152         </tr>
1153         <tr>
1154           <td> <i>Empty</i> </td>
1155           <td> <i>Form submit</i> </td>
1156         </tr>
1157     </table>
1158   </span>
1159   </span>
1160   </span>
1161
1162     <p>
1163       I will not reproduce all of the form inputs here:
1164     </p>
1165
1166 <pre>
1167 static void
1168 create_form (ml_session session, void *vp)
1169 {
1170   pool pool = ml_session_pool (session);
1171   struct data *data = (struct data *) vp;
1172   ml_form form;
1173   ml_table_layout tbl;
1174   ml_text_label text;
1175   ml_form_submit submit;
1176   ml_flow_layout flow;
1177   int i;
1178
1179   /* Create the form. */
1180   form = new_ml_form (pool);
1181   ml_form_set_callback (form, submit_form, session, data);
1182
1183   /* Create the table. */
1184   tbl = new_ml_table_layout (pool, 10, 2);
1185
1186   /* Create the contents of the form. */
1187   text = new_ml_text_label (pool, "Family name / surname");
1188   ml_table_layout_pack (tbl, text, 0, 0);
1189   data-&gt;familyname = new_ml_form_text (pool, form);
1190   ml_table_layout_pack (tbl, data-&gt;familyname, 0, 1);
1191
1192          :      :     :
1193          :      :     :
1194          :      :     :
1195
1196   /* Submit button. */
1197   submit = new_ml_form_submit (pool, form, "Submit");
1198   ml_table_layout_pack (tbl, submit, 9, 1);
1199
1200   /* Pack it all up. */
1201   ml_form_pack (form, tbl);
1202   ml_window_pack (data-&gt;win, form);
1203 }
1204 </pre>
1205
1206     <p>
1207       Notice that we set a callback function on the form:
1208     </p>
1209
1210 <pre>
1211 ml_form_set_callback (form, submit_form, session, data);
1212 </pre>
1213
1214     <p>
1215       When the form is submitted by the user, <code>submit_form
1216         (session, data)</code> will be called. <code>submit_form</code>,
1217       reproduced next, can read the value that the user entered
1218       into each form field by calling <code>ml_form_input_get_value</code>:
1219     </p>
1220
1221 <pre>
1222 static void
1223 submit_form (ml_session session, void *vp)
1224 {
1225   pool pool = ml_session_pool (session);
1226   struct data *data = (struct data *) vp;
1227   ml_text_label text;
1228   ml_table_layout tbl;
1229   ml_button button;
1230   const char *str;
1231
1232   str = psprintf
1233     (pool,
1234      "Form submitted.\n"
1235      "\n"
1236      "Family name: %s\n"
1237      "Given name: %s\n"
1238      "Password: %s\n"
1239      "Date of birth: dd = %d, mm = %d, yyyy = %d\n"
1240      "Gender: %s\n"
1241      "   M is checked: %d  F is checked: %d\n"
1242      "Interests: Eating = %d, Drinking = %d, Sleeping = %d\n"
1243      "Dept fields checked: [ %s ]\n"
1244      "Comments:\n"
1245      "--start--\n"
1246      "%s\n"
1247      "--end--\n",
1248      ml_form_input_get_value (data-&gt;familyname),
1249      ml_form_input_get_value (data-&gt;givenname),
1250      ml_form_input_get_value (data-&gt;password),
1251      1 + ml_form_select_get_selection (data-&gt;dd),
1252      1 + ml_form_select_get_selection (data-&gt;mm),
1253      1900 + ml_form_select_get_selection (data-&gt;yyyy),
1254      ml_form_input_get_value (data-&gt;gender),
1255      ml_form_radio_get_checked (data-&gt;m),
1256      ml_form_radio_get_checked (data-&gt;f),
1257      ml_form_input_get_value (data-&gt;eating) ? 1 : 0,
1258      ml_form_input_get_value (data-&gt;drinking) ? 1 : 0,
1259      ml_form_input_get_value (data-&gt;sleeping) ? 1 : 0,
1260      pjoin (pool,
1261             pvitostr (pool, ml_form_select_get_selections (data-&gt;dept)), ", "),
1262      ml_form_input_get_value (data-&gt;comments));
1263
1264   tbl = new_ml_table_layout (pool, 2, 1);
1265   text = new_ml_text_label (pool, str);
1266   ml_table_layout_pack (tbl, text, 0, 0);
1267   button = new_ml_button (pool, "Back to form");
1268   ml_button_set_callback (button, create_form, session, data);
1269   ml_table_layout_pack (tbl, button, 1, 0);
1270
1271   ml_window_pack (data-&gt;win, tbl);
1272 }
1273 </pre>
1274
1275     <p>
1276       Notice how we have added a button called <q>Back to form</q>
1277       which calls <code>create_form</code>.
1278     </p>
1279
1280     <h3>The end of this tutorial</h3>
1281
1282     <p>
1283       That's the end of this tutorial. You should be able to
1284       go and write <code>monolith</code> applications and
1285       widgets now. Full manual pages are included below.
1286     </p>
1287
1288     <h2>The monolith class hierarchy</h2>
1289
1290     <p>
1291       Of course <code>monolith</code> is written in C, so there are no
1292       classes <i>per se</i>, but this is the general class hierarchy of
1293       <code>monolith</code> widgets and windows:
1294     </p>
1295
1296 <pre>
1297 ml_window: window or frameset
1298
1299 ml_session: user session
1300
1301 ml_widget
1302     |
1303     |
1304     +-- ml_box: draws a box around another widget
1305     |
1306     +-- ml_button: simple button
1307     |
1308     +-- ml_dialog: ask the user a question
1309     |
1310     +-- ml_flow_layout: layout widgets one after another
1311     |
1312     +-- ml_form: surrounds a collection of form inputs
1313     |
1314     +-- ml_form_input
1315     |       |
1316     |       |
1317     |       +-- ml_form_checkbox: checkbox (tickbox)
1318     |       |
1319     |       +-- ml_form_file: file upload input [not implemented]
1320     |       |
1321     |       +-- ml_form_password: single line password input
1322     |       |
1323     |       +-- ml_form_radio_group: group of radio buttons
1324     |       |
1325     |       +-- ml_form_radio: radio button
1326     |       |
1327     |       +-- ml_form_select: drop-down list of options
1328     |       |
1329     |       +-- ml_form_submit: submit button
1330     |       |
1331     |       +-- ml_form_text: single line text input
1332     |       |
1333     |       +-- ml_form_textarea: multi-line text input
1334     |
1335     +-- ml_image: display an icon or image
1336     |
1337     +-- ml_label: display arbitrary HTML
1338     |
1339     +-- ml_table_layout: powerful table layouts of widgets
1340     |
1341     +-- ml_text_label: single line or paragraphs of plain text
1342     |
1343     +-- ml_toggle_button: toggle button
1344 </pre>
1345
1346     <p>
1347       Inheritance is faked using a technique very similar to the vtables
1348       used by C++.
1349     </p>
1350
1351     <h2>Other Notes</h2>
1352
1353     <h3>Can I theme monolith applications?</h3>
1354
1355     <p>
1356       Yes, you can. At the moment, the easiest way to change the look
1357       and feel is to edit the default stylesheet
1358       (<code>default.css</code>). This way you can make extensive
1359       changes to how your application looks from a single file.
1360     </p>
1361
1362     <p>
1363       If you don't like the idea of editing <code>default.css</code>,
1364       then copy it and make your own. Call
1365       <code>ml_window_set_stylesheet</code> on all your application
1366       windows to point to your new stylesheet.
1367     </p>
1368
1369     <p>
1370       You can even provide different themes to different users
1371       (so-called "skinning" - ugh I hate that term). Once a user has
1372       logged into the app, call <code>ml_window_set_stylesheet</code>
1373       with the appropriate theme for that user.
1374     </p>
1375
1376     <h3>Frames considered harmful</h3>
1377
1378     <p>
1379       Although <code>monolith</code> supports frames and pop-up
1380       windows, care should be taken because these do not work in
1381       the way that most users expect.
1382     </p>
1383
1384     <p>
1385       The problem arises when one frame tries to update the state
1386       of another frame (the same problem applies to two separate
1387       windows, but I'll just use the generic term "frame" here).
1388       For example, imagine the following simple frameset:
1389     </p>
1390
1391     <table border="1">
1392         <tr>
1393           <td style="width: 100px">
1394             <p><i>Left frame</i></p>
1395             <p>[Button 1]</p>
1396             <p>[Button 2]</p>
1397             <p>[Button 3]</p>
1398           </td>
1399           <td style="width: 200px" valign="top">
1400             <p><i>Right frame</i></p>
1401           </td>
1402         </tr>
1403     </table>
1404
1405     <p>
1406       It's common to want the buttons in the left hand frame to
1407       change the contents displayed in the right hand frame, and
1408       a naive way to do this would be to set the callback for
1409       each button to a function like this:
1410     </p>
1411
1412 <pre>
1413 static void
1414 update_right_frame (ml_session session, void *vp)
1415 {
1416   /* Get private per-session data. */
1417   struct data *data = (struct data *) vp;
1418
1419   pool pool = ml_session_pool (session);
1420
1421   ml_text_label label = new_ml_text_label (pool, <i>updated content</i>);
1422
1423   /* Change the contents of the right hand frame. */
1424   ml_window_pack (data-&gt;right_frame, label);
1425 }
1426 </pre>
1427
1428     <p>
1429       The problem is that this doesn't work at all. Pressing the
1430       buttons will not update the right hand frame.
1431     </p>
1432
1433     <p>
1434       For seasoned HTML programmers, it will be obvious why this
1435       happens. For people used to traditional application development,
1436       it is confusing and seems like a bug.
1437     </p>
1438
1439     <p>
1440       In future, we will add features to Monolith to allow careful
1441       developers to use frames in situations such as above. However
1442       at the moment, the advice is:
1443     </p>
1444
1445     <ol>
1446       <li> Consider each frame/window/pop-up to be a completely
1447         separate entity, almost like a separate session. Don't
1448         expect that updating properties in another frame/window/pop-up
1449         will work (it almost certainly will have no effect).
1450       <li> Avoid using framesets if possible. Table layouts are
1451         often a better substitute.
1452     </ol>
1453
1454     <h3>Notes on forms</h3>
1455
1456     <p>
1457       You cannot nest forms. (This is a limitation of HTML.)
1458     </p>
1459
1460     <p>
1461       If you have several forms on the same page, you can
1462       run into problems. A common problem happens when you
1463       have two forms on the same dialog like this:
1464     </p>
1465
1466     <table border="1">
1467         <tr>
1468           <td>
1469             <i>First form</i><br>
1470             [ --- input #1 --- ] [submit]
1471           </td>
1472         </tr>
1473         <tr>
1474           <td>
1475             <i>Second form</i><br>
1476             [ --- input #2 --- ] [submit]
1477           </td>
1478         </tr>
1479     </table>
1480
1481     <p>
1482       If the user types something in input field #1, then types
1483       something in input field #2, and presses the second submit
1484       button, then the contents of input field #1 will disappear.
1485     </p>
1486
1487     <p>
1488       To avoid this, either only use one form on a dialog, or design
1489       your dialogs so that it is clear that the first submit button
1490       must be pressed after filling out the first form.
1491     </p>
1492
1493     <p>
1494       Another problem with forms (and again, a limitation of HTML)
1495       is that they are not interactive. The server cannot read the
1496       value of a form input field until the [Submit] button has
1497       been pressed.
1498     </p>
1499
1500     <p>
1501       If you are using forms, then only use form input widgets
1502       inside them. Toggle buttons and so on are not form input
1503       widgets, and cannot be part of a form.
1504     </p>
1505
1506     <h2>Links to manual pages</h2>
1507
1508     <ul>
1509       <li> <a href="ml_box_pack.3.html"><code>ml_box_pack(3)</code></a> </li>
1510       <li> <a href="ml_button_get_text.3.html"><code>ml_button_get_text(3)</code></a> </li>
1511       <li> <a href="ml_button_set_callback.3.html"><code>ml_button_set_callback(3)</code></a> </li>
1512       <li> <a href="ml_button_set_key.3.html"><code>ml_button_set_key(3)</code></a> </li>
1513       <li> <a href="ml_button_set_text.3.html"><code>ml_button_set_text(3)</code></a> </li>
1514       <li> <a href="ml_dialog_add_button.3.html"><code>ml_dialog_add_button(3)</code></a> </li>
1515       <li> <a href="ml_dialog_clear_buttons.3.html"><code>ml_dialog_clear_buttons(3)</code></a> </li>
1516       <li> <a href="ml_dialog_get_icon.3.html"><code>ml_dialog_get_icon(3)</code></a> </li>
1517       <li> <a href="ml_dialog_get_text.3.html"><code>ml_dialog_get_text(3)</code></a> </li>
1518       <li> <a href="ml_dialog_get_title.3.html"><code>ml_dialog_get_title(3)</code></a> </li>
1519       <li> <a href="ml_dialog_set_icon.3.html"><code>ml_dialog_set_icon(3)</code></a> </li>
1520       <li> <a href="ml_dialog_set_text.3.html"><code>ml_dialog_set_text(3)</code></a> </li>
1521       <li> <a href="ml_dialog_set_title.3.html"><code>ml_dialog_set_title(3)</code></a> </li>
1522       <li> <a href="ml_entry_point.3.html"><code>ml_entry_point(3)</code></a> </li>
1523       <li> <a href="ml_flow_layout_clear.3.html"><code>ml_flow_layout_clear(3)</code></a> </li>
1524       <li> <a href="ml_flow_layout_erase.3.html"><code>ml_flow_layout_erase(3)</code></a> </li>
1525       <li> <a href="ml_flow_layout_get.3.html"><code>ml_flow_layout_get(3)</code></a> </li>
1526       <li> <a href="ml_flow_layout_insert.3.html"><code>ml_flow_layout_insert(3)</code></a> </li>
1527       <li> <a href="ml_flow_layout_pack.3.html"><code>ml_flow_layout_pack(3)</code></a> </li>
1528       <li> <a href="ml_flow_layout_pop_back.3.html"><code>ml_flow_layout_pop_back(3)</code></a> </li>
1529       <li> <a href="ml_flow_layout_pop_front.3.html"><code>ml_flow_layout_pop_front(3)</code></a> </li>
1530       <li> <a href="ml_flow_layout_push_back.3.html"><code>ml_flow_layout_push_back(3)</code></a> </li>
1531       <li> <a href="ml_flow_layout_push_front.3.html"><code>ml_flow_layout_push_front(3)</code></a> </li>
1532       <li> <a href="ml_flow_layout_replace.3.html"><code>ml_flow_layout_replace(3)</code></a> </li>
1533       <li> <a href="ml_flow_layout_size.3.html"><code>ml_flow_layout_size(3)</code></a> </li>
1534       <li> <a href="ml_form_input_clear_value.3.html"><code>ml_form_input_clear_value(3)</code></a> </li>
1535       <li> <a href="ml_form_input_get_value.3.html"><code>ml_form_input_get_value(3)</code></a> </li>
1536       <li> <a href="ml_form_input_set_value.3.html"><code>ml_form_input_set_value(3)</code></a> </li>
1537       <li> <a href="ml_form_radio_get_checked.3.html"><code>ml_form_radio_get_checked(3)</code></a> </li>
1538       <li> <a href="ml_form_radio_group_pack.3.html"><code>ml_form_radio_group_pack(3)</code></a> </li>
1539       <li> <a href="ml_form_radio_set_checked.3.html"><code>ml_form_radio_set_checked(3)</code></a> </li>
1540       <li> <a href="ml_form_select_clear.3.html"><code>ml_form_select_clear(3)</code></a> </li>
1541       <li> <a href="ml_form_select_erase.3.html"><code>ml_form_select_erase(3)</code></a> </li>
1542       <li> <a href="ml_form_select_get.3.html"><code>ml_form_select_get(3)</code></a> </li>
1543       <li> <a href="ml_form_select_get_multiple.3.html"><code>ml_form_select_get_multiple(3)</code></a> </li>
1544       <li> <a href="ml_form_select_get_selection.3.html"><code>ml_form_select_get_selection(3)</code></a> </li>
1545       <li> <a href="ml_form_select_get_selections.3.html"><code>ml_form_select_get_selections(3)</code></a> </li>
1546       <li> <a href="ml_form_select_get_size.3.html"><code>ml_form_select_get_size(3)</code></a> </li>
1547       <li> <a href="ml_form_select_insert.3.html"><code>ml_form_select_insert(3)</code></a> </li>
1548       <li> <a href="ml_form_select_pop_back.3.html"><code>ml_form_select_pop_back(3)</code></a> </li>
1549       <li> <a href="ml_form_select_pop_front.3.html"><code>ml_form_select_pop_front(3)</code></a> </li>
1550       <li> <a href="ml_form_select_push_back.3.html"><code>ml_form_select_push_back(3)</code></a> </li>
1551       <li> <a href="ml_form_select_push_front.3.html"><code>ml_form_select_push_front(3)</code></a> </li>
1552       <li> <a href="ml_form_select_replace.3.html"><code>ml_form_select_replace(3)</code></a> </li>
1553       <li> <a href="ml_form_select_set_multiple.3.html"><code>ml_form_select_set_multiple(3)</code></a> </li>
1554       <li> <a href="ml_form_select_set_selection.3.html"><code>ml_form_select_set_selection(3)</code></a> </li>
1555       <li> <a href="ml_form_select_set_selections.3.html"><code>ml_form_select_set_selections(3)</code></a> </li>
1556       <li> <a href="ml_form_select_set_size.3.html"><code>ml_form_select_set_size(3)</code></a> </li>
1557       <li> <a href="ml_form_select_size.3.html"><code>ml_form_select_size(3)</code></a> </li>
1558       <li> <a href="ml_frameset_get_title.3.html"><code>ml_frameset_get_title(3)</code></a> </li>
1559       <li> <a href="ml_frameset_set_description.3.html"><code>ml_frameset_set_description(3)</code></a> </li>
1560       <li> <a href="ml_frameset_set_title.3.html"><code>ml_frameset_set_title(3)</code></a> </li>
1561       <li> <a href="ml_image_get_src.3.html"><code>ml_image_get_src(3)</code></a> </li>
1562       <li> <a href="ml_image_set_src.3.html"><code>ml_image_set_src(3)</code></a> </li>
1563       <li> <a href="ml_label_get_text.3.html"><code>ml_label_get_text(3)</code></a> </li>
1564       <li> <a href="ml_label_set_text.3.html"><code>ml_label_set_text(3)</code></a> </li>
1565       <li> <a href="ml_register_action.3.html"><code>ml_register_action(3)</code></a> </li>
1566       <li> <a href="ml_session_args.3.html"><code>ml_session_args(3)</code></a> </li>
1567       <li> <a href="ml_session_canonical_path.3.html"><code>ml_session_canonical_path(3)</code></a> </li>
1568       <li> <a href="ml_session_pool.3.html"><code>ml_session_pool(3)</code></a> </li>
1569       <li> <a href="ml_session_script_name.3.html"><code>ml_session_script_name(3)</code></a> </li>
1570       <li> <a href="ml_session_sessionid.3.html"><code>ml_session_sessionid(3)</code></a> </li>
1571       <li> <a href="ml_table_layout_pack.3.html"><code>ml_table_layout_pack(3)</code></a> </li>
1572       <li> <a href="ml_table_layout_set_align.3.html"><code>ml_table_layout_set_align(3)</code></a> </li>
1573       <li> <a href="ml_table_layout_set_colspan.3.html"><code>ml_table_layout_set_colspan(3)</code></a> </li>
1574       <li> <a href="ml_table_layout_set_rowspan.3.html"><code>ml_table_layout_set_rowspan(3)</code></a> </li>
1575       <li> <a href="ml_table_layout_set_valign.3.html"><code>ml_table_layout_set_valign(3)</code></a> </li>
1576       <li> <a href="ml_text_label_set_font_size.3.html"><code>ml_text_label_set_font_size(3)</code></a> </li>
1577       <li> <a href="ml_text_label_set_font_weight.3.html"><code>ml_text_label_set_font_weight(3)</code></a> </li>
1578       <li> <a href="ml_text_label_set_text.3.html"><code>ml_text_label_set_text(3)</code></a> </li>
1579       <li> <a href="ml_text_label_set_text_align.3.html"><code>ml_text_label_set_text_align(3)</code></a> </li>
1580       <li> <a href="ml_unregister_action.3.html"><code>ml_unregister_action(3)</code></a> </li>
1581       <li> <a href="ml_widget_repaint.3.html"><code>ml_widget_repaint(3)</code></a> </li>
1582       <li> <a href="ml_window_get_charset.3.html"><code>ml_window_get_charset(3)</code></a> </li>
1583       <li> <a href="ml_window_get_stylesheet.3.html"><code>ml_window_get_stylesheet(3)</code></a> </li>
1584       <li> <a href="ml_window_get_title.3.html"><code>ml_window_get_title(3)</code></a> </li>
1585       <li> <a href="ml_window_pack.3.html"><code>ml_window_pack(3)</code></a> </li>
1586       <li> <a href="ml_window_set_charset.3.html"><code>ml_window_set_charset(3)</code></a> </li>
1587       <li> <a href="ml_window_set_stylesheet.3.html"><code>ml_window_set_stylesheet(3)</code></a> </li>
1588       <li> <a href="ml_window_set_title.3.html"><code>ml_window_set_title(3)</code></a> </li>
1589       <li> <a href="new_ml_box.3.html"><code>new_ml_box(3)</code></a> </li>
1590       <li> <a href="new_ml_button.3.html"><code>new_ml_button(3)</code></a> </li>
1591       <li> <a href="new_ml_dialog.3.html"><code>new_ml_dialog(3)</code></a> </li>
1592       <li> <a href="new_ml_flow_layout.3.html"><code>new_ml_flow_layout(3)</code></a> </li>
1593       <li> <a href="new_ml_form.3.html"><code>new_ml_form(3)</code></a> </li>
1594       <li> <a href="new_ml_form_checkbox.3.html"><code>new_ml_form_checkbox(3)</code></a> </li>
1595       <li> <a href="new_ml_form_password.3.html"><code>new_ml_form_password(3)</code></a> </li>
1596       <li> <a href="new_ml_form_radio.3.html"><code>new_ml_form_radio(3)</code></a> </li>
1597       <li> <a href="new_ml_form_radio_group.3.html"><code>new_ml_form_radio_group(3)</code></a> </li>
1598       <li> <a href="new_ml_form_select.3.html"><code>new_ml_form_select(3)</code></a> </li>
1599       <li> <a href="new_ml_form_submit.3.html"><code>new_ml_form_submit(3)</code></a> </li>
1600       <li> <a href="new_ml_form_text.3.html"><code>new_ml_form_text(3)</code></a> </li>
1601       <li> <a href="new_ml_form_textarea.3.html"><code>new_ml_form_textarea(3)</code></a> </li>
1602       <li> <a href="new_ml_frameset.3.html"><code>new_ml_frameset(3)</code></a> </li>
1603       <li> <a href="new_ml_image.3.html"><code>new_ml_image(3)</code></a> </li>
1604       <li> <a href="new_ml_label.3.html"><code>new_ml_label(3)</code></a> </li>
1605       <li> <a href="new_ml_table_layout.3.html"><code>new_ml_table_layout(3)</code></a> </li>
1606       <li> <a href="new_ml_text_label.3.html"><code>new_ml_text_label(3)</code></a> </li>
1607       <li> <a href="new_ml_window.3.html"><code>new_ml_window(3)</code></a> </li>
1608     </ul>
1609
1610     <hr>
1611     <address><a href="mailto:rich@annexia.org">Richard Jones</a></address>
1612 <!-- Created: Wed May  1 19:36:16 BST 2002 -->
1613 <!-- hhmts start -->
1614 Last modified: Sat Sep  7 14:45:50 BST 2002
1615 <!-- hhmts end -->
1616   </body>
1617 </html>