Add to git. master
authorRichard W.M. Jones <rjones@redhat.com>
Fri, 25 Apr 2014 10:40:00 +0000 (11:40 +0100)
committerRichard W.M. Jones <rjones@redhat.com>
Fri, 25 Apr 2014 10:40:00 +0000 (11:40 +0100)
275 files changed:
.cvsignore [new file with mode: 0644]
COPYING.LIB [new file with mode: 0644]
Makefile+ [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
apps/README [new file with mode: 0644]
apps/msp.c [new file with mode: 0644]
apps/stats.c [new file with mode: 0644]
calendar/.cvsignore [new file with mode: 0644]
calendar/ml_calendar.c [new file with mode: 0644]
calendar/ml_calendar.h [new file with mode: 0644]
calendar/ml_calendar_day.c [new file with mode: 0644]
calendar/ml_calendar_day.h [new file with mode: 0644]
calendar/ml_calendar_lib.c [new file with mode: 0644]
calendar/ml_calendar_lib.h [new file with mode: 0644]
calendar/ml_calendar_month.c [new file with mode: 0644]
calendar/ml_calendar_month.h [new file with mode: 0644]
calendar/ml_calendar_notes.c [new file with mode: 0644]
calendar/ml_calendar_notes.h [new file with mode: 0644]
calendar/ml_calendar_visible.c [new file with mode: 0644]
calendar/ml_calendar_visible.h [new file with mode: 0644]
chat/.cvsignore [new file with mode: 0644]
chat/README.chatbot-api [new file with mode: 0644]
chat/bots/chatbotlib.pl [new file with mode: 0644]
chat/bots/kharmabot.pl [new file with mode: 0644]
chat/chatbot.c [new file with mode: 0644]
chat/chatroom.c [new file with mode: 0644]
chat/chatroom.h [new file with mode: 0644]
chat/lib.c [new file with mode: 0644]
chat/lib.h [new file with mode: 0644]
chat/message.c [new file with mode: 0644]
chat/message.h [new file with mode: 0644]
chat/messages_pane.c [new file with mode: 0644]
chat/messages_pane.h [new file with mode: 0644]
chat/ml_chat_button.c [new file with mode: 0644]
chat/ml_chat_button.h [new file with mode: 0644]
chat/ml_chat_window.c [new file with mode: 0644]
chat/ml_chat_window.h [new file with mode: 0644]
chat/thread.c [new file with mode: 0644]
chat/users_pane.c [new file with mode: 0644]
chat/users_pane.h [new file with mode: 0644]
configure [new file with mode: 0755]
default.css [new file with mode: 0644]
discussion/.cvsignore [new file with mode: 0644]
discussion/ml_discussion.c [new file with mode: 0644]
discussion/ml_discussion.h [new file with mode: 0644]
discussion/ml_discussion_panel.c [new file with mode: 0644]
discussion/ml_discussion_panel.h [new file with mode: 0644]
doc/hello.c [new file with mode: 0644]
doc/index.html [new file with mode: 0644]
doc/ml_box_pack.3.html [new file with mode: 0644]
doc/ml_button_get_text.3.html [new file with mode: 0644]
doc/ml_button_set_callback.3.html [new file with mode: 0644]
doc/ml_button_set_key.3.html [new file with mode: 0644]
doc/ml_button_set_text.3.html [new file with mode: 0644]
doc/ml_dialog_add_button.3.html [new file with mode: 0644]
doc/ml_dialog_clear_buttons.3.html [new file with mode: 0644]
doc/ml_dialog_get_icon.3.html [new file with mode: 0644]
doc/ml_dialog_get_text.3.html [new file with mode: 0644]
doc/ml_dialog_get_title.3.html [new file with mode: 0644]
doc/ml_dialog_set_icon.3.html [new file with mode: 0644]
doc/ml_dialog_set_text.3.html [new file with mode: 0644]
doc/ml_dialog_set_title.3.html [new file with mode: 0644]
doc/ml_entry_point.3.html [new file with mode: 0644]
doc/ml_flow_layout_clear.3.html [new file with mode: 0644]
doc/ml_flow_layout_erase.3.html [new file with mode: 0644]
doc/ml_flow_layout_get.3.html [new file with mode: 0644]
doc/ml_flow_layout_insert.3.html [new file with mode: 0644]
doc/ml_flow_layout_pack.3.html [new file with mode: 0644]
doc/ml_flow_layout_pop_back.3.html [new file with mode: 0644]
doc/ml_flow_layout_pop_front.3.html [new file with mode: 0644]
doc/ml_flow_layout_push_back.3.html [new file with mode: 0644]
doc/ml_flow_layout_push_front.3.html [new file with mode: 0644]
doc/ml_flow_layout_replace.3.html [new file with mode: 0644]
doc/ml_flow_layout_size.3.html [new file with mode: 0644]
doc/ml_form_input_clear_value.3.html [new file with mode: 0644]
doc/ml_form_input_get_value.3.html [new file with mode: 0644]
doc/ml_form_input_set_value.3.html [new file with mode: 0644]
doc/ml_form_radio_get_checked.3.html [new file with mode: 0644]
doc/ml_form_radio_group_pack.3.html [new file with mode: 0644]
doc/ml_form_radio_set_checked.3.html [new file with mode: 0644]
doc/ml_form_select_clear.3.html [new file with mode: 0644]
doc/ml_form_select_erase.3.html [new file with mode: 0644]
doc/ml_form_select_get.3.html [new file with mode: 0644]
doc/ml_form_select_get_multiple.3.html [new file with mode: 0644]
doc/ml_form_select_get_selection.3.html [new file with mode: 0644]
doc/ml_form_select_get_selections.3.html [new file with mode: 0644]
doc/ml_form_select_get_size.3.html [new file with mode: 0644]
doc/ml_form_select_insert.3.html [new file with mode: 0644]
doc/ml_form_select_pop_back.3.html [new file with mode: 0644]
doc/ml_form_select_pop_front.3.html [new file with mode: 0644]
doc/ml_form_select_push_back.3.html [new file with mode: 0644]
doc/ml_form_select_push_front.3.html [new file with mode: 0644]
doc/ml_form_select_replace.3.html [new file with mode: 0644]
doc/ml_form_select_set_multiple.3.html [new file with mode: 0644]
doc/ml_form_select_set_selection.3.html [new file with mode: 0644]
doc/ml_form_select_set_selections.3.html [new file with mode: 0644]
doc/ml_form_select_set_size.3.html [new file with mode: 0644]
doc/ml_form_select_size.3.html [new file with mode: 0644]
doc/ml_frameset_get_title.3.html [new file with mode: 0644]
doc/ml_frameset_set_description.3.html [new file with mode: 0644]
doc/ml_frameset_set_title.3.html [new file with mode: 0644]
doc/ml_image_get_src.3.html [new file with mode: 0644]
doc/ml_image_set_src.3.html [new file with mode: 0644]
doc/ml_label_get_text.3.html [new file with mode: 0644]
doc/ml_label_set_text.3.html [new file with mode: 0644]
doc/ml_register_action.3.html [new file with mode: 0644]
doc/ml_session_args.3.html [new file with mode: 0644]
doc/ml_session_canonical_path.3.html [new file with mode: 0644]
doc/ml_session_pool.3.html [new file with mode: 0644]
doc/ml_session_script_name.3.html [new file with mode: 0644]
doc/ml_session_sessionid.3.html [new file with mode: 0644]
doc/ml_table_layout_pack.3.html [new file with mode: 0644]
doc/ml_table_layout_set_align.3.html [new file with mode: 0644]
doc/ml_table_layout_set_colspan.3.html [new file with mode: 0644]
doc/ml_table_layout_set_rowspan.3.html [new file with mode: 0644]
doc/ml_table_layout_set_valign.3.html [new file with mode: 0644]
doc/ml_text_label_set_font_size.3.html [new file with mode: 0644]
doc/ml_text_label_set_font_weight.3.html [new file with mode: 0644]
doc/ml_text_label_set_text.3.html [new file with mode: 0644]
doc/ml_text_label_set_text_align.3.html [new file with mode: 0644]
doc/ml_unregister_action.3.html [new file with mode: 0644]
doc/ml_widget_repaint.3.html [new file with mode: 0644]
doc/ml_window_get_charset.3.html [new file with mode: 0644]
doc/ml_window_get_stylesheet.3.html [new file with mode: 0644]
doc/ml_window_get_title.3.html [new file with mode: 0644]
doc/ml_window_pack.3.html [new file with mode: 0644]
doc/ml_window_set_charset.3.html [new file with mode: 0644]
doc/ml_window_set_stylesheet.3.html [new file with mode: 0644]
doc/ml_window_set_title.3.html [new file with mode: 0644]
doc/new_ml_box.3.html [new file with mode: 0644]
doc/new_ml_button.3.html [new file with mode: 0644]
doc/new_ml_dialog.3.html [new file with mode: 0644]
doc/new_ml_flow_layout.3.html [new file with mode: 0644]
doc/new_ml_form.3.html [new file with mode: 0644]
doc/new_ml_form_checkbox.3.html [new file with mode: 0644]
doc/new_ml_form_password.3.html [new file with mode: 0644]
doc/new_ml_form_radio.3.html [new file with mode: 0644]
doc/new_ml_form_radio_group.3.html [new file with mode: 0644]
doc/new_ml_form_select.3.html [new file with mode: 0644]
doc/new_ml_form_submit.3.html [new file with mode: 0644]
doc/new_ml_form_text.3.html [new file with mode: 0644]
doc/new_ml_form_textarea.3.html [new file with mode: 0644]
doc/new_ml_frameset.3.html [new file with mode: 0644]
doc/new_ml_image.3.html [new file with mode: 0644]
doc/new_ml_label.3.html [new file with mode: 0644]
doc/new_ml_table_layout.3.html [new file with mode: 0644]
doc/new_ml_text_label.3.html [new file with mode: 0644]
doc/new_ml_window.3.html [new file with mode: 0644]
examples/01_label_and_button.c [new file with mode: 0644]
examples/02_toy_calculator.c [new file with mode: 0644]
examples/03_many_toy_calculators.c [new file with mode: 0644]
examples/04_animal_vegetable_mineral.c [new file with mode: 0644]
examples/05_popup_windows_and_frames.c [new file with mode: 0644]
examples/06_big_form.c [new file with mode: 0644]
examples/07_toggle_buttons.c [new file with mode: 0644]
examples/08_menus.c [new file with mode: 0644]
examples/expenses.c [new file with mode: 0644]
examples/expenses_widget.c [new file with mode: 0644]
examples/expenses_widget.h [new file with mode: 0644]
examples/questions_database [new file with mode: 0644]
examples/toy_calculator.c [new file with mode: 0644]
examples/toy_calculator.h [new file with mode: 0644]
icons/addrbook.png [new file with mode: 0644]
icons/addrbook.ppm [new file with mode: 0644]
icons/ledge.png [new file with mode: 0644]
icons/ledge.ppm [new file with mode: 0644]
icons/ledgeu.png [new file with mode: 0644]
icons/ledgeu.ppm [new file with mode: 0644]
icons/loverr.png [new file with mode: 0644]
icons/loverr.ppm [new file with mode: 0644]
icons/loverru.png [new file with mode: 0644]
icons/loverru.ppm [new file with mode: 0644]
icons/redge.png [new file with mode: 0644]
icons/redge.ppm [new file with mode: 0644]
icons/redgeu.png [new file with mode: 0644]
icons/redgeu.ppm [new file with mode: 0644]
icons/roverl.png [new file with mode: 0644]
icons/roverl.ppm [new file with mode: 0644]
icons/roverlu.png [new file with mode: 0644]
icons/roverlu.ppm [new file with mode: 0644]
sql/ml_bulletins_create.sql [new file with mode: 0644]
sql/ml_bulletins_drop.sql [new file with mode: 0644]
sql/ml_calendar_create.sql [new file with mode: 0644]
sql/ml_calendar_drop.sql [new file with mode: 0644]
sql/ml_chat_create.sql [new file with mode: 0644]
sql/ml_chat_drop.sql [new file with mode: 0644]
sql/ml_discussion_create.sql [new file with mode: 0644]
sql/ml_discussion_drop.sql [new file with mode: 0644]
sql/ml_discussion_functions.sql [new file with mode: 0644]
sql/ml_user_directory_create.sql [new file with mode: 0644]
sql/monolith_auth_create.sql [new file with mode: 0644]
sql/monolith_auth_drop.sql [new file with mode: 0644]
sql/monolith_core_create.sql [new file with mode: 0644]
sql/monolith_core_drop.sql [new file with mode: 0644]
sql/monolith_resources_create.sql [new file with mode: 0644]
sql/monolith_resources_drop.sql [new file with mode: 0644]
sql/monolith_users_create.sql [new file with mode: 0644]
sql/monolith_users_drop.sql [new file with mode: 0644]
src/ml_box.c [new file with mode: 0644]
src/ml_box.h [new file with mode: 0644]
src/ml_button.c [new file with mode: 0644]
src/ml_button.h [new file with mode: 0644]
src/ml_close_button.c [new file with mode: 0644]
src/ml_close_button.h [new file with mode: 0644]
src/ml_dialog.c [new file with mode: 0644]
src/ml_dialog.h [new file with mode: 0644]
src/ml_flow_layout.c [new file with mode: 0644]
src/ml_flow_layout.h [new file with mode: 0644]
src/ml_form.c [new file with mode: 0644]
src/ml_form.h [new file with mode: 0644]
src/ml_form_checkbox.c [new file with mode: 0644]
src/ml_form_checkbox.h [new file with mode: 0644]
src/ml_form_input.c [new file with mode: 0644]
src/ml_form_input.h [new file with mode: 0644]
src/ml_form_layout.c [new file with mode: 0644]
src/ml_form_layout.h [new file with mode: 0644]
src/ml_form_password.c [new file with mode: 0644]
src/ml_form_password.h [new file with mode: 0644]
src/ml_form_radio.c [new file with mode: 0644]
src/ml_form_radio.h [new file with mode: 0644]
src/ml_form_radio_group.c [new file with mode: 0644]
src/ml_form_radio_group.h [new file with mode: 0644]
src/ml_form_select.c [new file with mode: 0644]
src/ml_form_select.h [new file with mode: 0644]
src/ml_form_submit.c [new file with mode: 0644]
src/ml_form_submit.h [new file with mode: 0644]
src/ml_form_text.c [new file with mode: 0644]
src/ml_form_text.h [new file with mode: 0644]
src/ml_form_textarea.c [new file with mode: 0644]
src/ml_form_textarea.h [new file with mode: 0644]
src/ml_heading.c [new file with mode: 0644]
src/ml_heading.h [new file with mode: 0644]
src/ml_horizontal_layout.c [new file with mode: 0644]
src/ml_horizontal_layout.h [new file with mode: 0644]
src/ml_iframe.c [new file with mode: 0644]
src/ml_iframe.h [new file with mode: 0644]
src/ml_image.c [new file with mode: 0644]
src/ml_image.h [new file with mode: 0644]
src/ml_label.c [new file with mode: 0644]
src/ml_label.h [new file with mode: 0644]
src/ml_menu.c [new file with mode: 0644]
src/ml_menu.h [new file with mode: 0644]
src/ml_multicol_layout.c [new file with mode: 0644]
src/ml_multicol_layout.h [new file with mode: 0644]
src/ml_select_layout.c [new file with mode: 0644]
src/ml_select_layout.h [new file with mode: 0644]
src/ml_smarttext.h [new file with mode: 0644]
src/ml_smarttext.l [new file with mode: 0644]
src/ml_tabbed_layout.c [new file with mode: 0644]
src/ml_tabbed_layout.h [new file with mode: 0644]
src/ml_table_layout.c [new file with mode: 0644]
src/ml_table_layout.h [new file with mode: 0644]
src/ml_text_label.c [new file with mode: 0644]
src/ml_text_label.h [new file with mode: 0644]
src/ml_toggle_button.c [new file with mode: 0644]
src/ml_toggle_button.h [new file with mode: 0644]
src/ml_vertical_layout.c [new file with mode: 0644]
src/ml_vertical_layout.h [new file with mode: 0644]
src/ml_widget.c [new file with mode: 0644]
src/ml_widget.h [new file with mode: 0644]
src/ml_window.c [new file with mode: 0644]
src/ml_window.h [new file with mode: 0644]
src/monolith.c [new file with mode: 0644]
src/monolith.h [new file with mode: 0644]
src/text.c [new file with mode: 0644]
widgets/ml_bulletins.c [new file with mode: 0644]
widgets/ml_bulletins.h [new file with mode: 0644]
widgets/ml_display_table.h [new file with mode: 0644]
widgets/ml_login_nopw.c [new file with mode: 0644]
widgets/ml_login_nopw.h [new file with mode: 0644]
widgets/ml_msp.c [new file with mode: 0644]
widgets/ml_msp.h [new file with mode: 0644]
widgets/ml_user_directory.c [new file with mode: 0644]
widgets/ml_user_directory.h [new file with mode: 0644]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..1964f99
--- /dev/null
@@ -0,0 +1 @@
+build-*
\ No newline at end of file
diff --git a/COPYING.LIB b/COPYING.LIB
new file mode 100644 (file)
index 0000000..eb685a5
--- /dev/null
@@ -0,0 +1,481 @@
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+                    675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+     Appendix: How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/Makefile+ b/Makefile+
new file mode 100644 (file)
index 0000000..2e889b7
--- /dev/null
+++ b/Makefile+
@@ -0,0 +1,650 @@
+# -*- Makefile -*-
+#
+# This is a make+ file. Make+ is a set of scripts which enhance GNU
+# make and let you build RPMs, and other package types with just one
+# control file.  To build this package you will need to download make+
+# from this site: http://www.annexia.org/freeware/makeplus/
+
+PACKAGE                := monolith
+VERSION_MAJOR  := 1
+VERSION_MINOR  := 2.0
+VERSION                := $(VERSION_MAJOR).$(VERSION_MINOR)
+
+SUMMARY                := a framework for web applications
+COPYRIGHT      := GNU LGPL
+AUTHOR         := Richard W.M. Jones <rich@annexia.org>
+
+define DESCRIPTION
+Monolith is a framework for web applications. Instead of thinking of
+your web application as a series of pages and forms, Monolith gives
+you basic widgets like buttons, images, form fields, tables and so on,
+which you can use to build up your web application or construct
+reusable "super-widgets". In this way, building a web application is
+rather like constructing a traditional GUI application using Gtk,
+Motif, Windows/MFC, Java/JFC, etc.
+
+Monolith applications are written in C or C++ (we may support other
+languages in future, particularly Perl and Java).
+
+Monolith applications compile down to standalone shared object scripts
+which can be run directly from the rws micro web server.
+
+Monolith comes with a full featured discussion system, chat server and
+calendar.
+endef
+
+RPM_REQUIRES   := rws >= 1.2.0, pthrlib >= 3.3.0, c2lib >= 1.4.0
+RPM_GROUP      := Development/Libraries
+
+iconsdir       = $(datadir)/rws/ml-icons
+solibdir       = $(datadir)/rws/so-bin
+sqldir         = $(datadir)/rws/sql
+styledir       = $(datadir)/rws/ml-styles
+symtabsdir     = $(datadir)/rws/symtabs
+botsdir                = $(pkgdatadir)/chat/bots
+
+CFLAGS         += -Wall -Werror -g -O2 -I$(srcdir)/src -I$(srcdir)/widgets \
+                  -DSYMTABSDIR=\"$(symtabsdir)\" -D_GNU_SOURCE \
+                  -I$(includedir)/c2lib
+ifneq ($(shell uname), SunOS)
+# Avoid a warning about reordering system include paths.
+CFLAGS         += $(shell pcre-config --cflags)
+endif
+
+LIBS           += -lrws -lpthrlib -lc2lib -lpq $(shell pcre-config --libs) -lm
+
+LOBJS  := src/ml_smarttext.lo \
+          src/text.lo \
+          src/monolith.lo \
+          src/ml_box.lo \
+          src/ml_button.lo \
+          src/ml_close_button.lo \
+          src/ml_dialog.lo \
+          src/ml_flow_layout.lo \
+          src/ml_form.lo \
+          src/ml_form_checkbox.lo \
+          src/ml_form_input.lo \
+          src/ml_form_layout.lo \
+          src/ml_form_password.lo \
+          src/ml_form_radio.lo \
+          src/ml_form_radio_group.lo \
+          src/ml_form_select.lo \
+          src/ml_form_submit.lo \
+          src/ml_form_text.lo \
+          src/ml_form_textarea.lo \
+          src/ml_heading.lo \
+          src/ml_horizontal_layout.lo \
+          src/ml_iframe.lo \
+          src/ml_image.lo \
+          src/ml_label.lo \
+          src/ml_menu.lo \
+          src/ml_multicol_layout.lo \
+          src/ml_select_layout.lo \
+          src/ml_table_layout.lo \
+          src/ml_text_label.lo \
+          src/ml_toggle_button.lo\
+          src/ml_vertical_layout.lo \
+          src/ml_widget.lo \
+          src/ml_window.lo
+
+HEADERS        := $(srcdir)/src/ml_smarttext.h \
+          $(srcdir)/src/monolith.h \
+          $(srcdir)/src/ml_box.h \
+          $(srcdir)/src/ml_button.h \
+          $(srcdir)/src/ml_close_button.h \
+          $(srcdir)/src/ml_dialog.h \
+          $(srcdir)/src/ml_flow_layout.h \
+          $(srcdir)/src/ml_form.h \
+          $(srcdir)/src/ml_form_checkbox.h \
+          $(srcdir)/src/ml_form_input.h \
+          $(srcdir)/src/ml_form_layout.h \
+          $(srcdir)/src/ml_form_password.h \
+          $(srcdir)/src/ml_form_radio.h \
+          $(srcdir)/src/ml_form_radio_group.h \
+          $(srcdir)/src/ml_form_select.h \
+          $(srcdir)/src/ml_form_submit.h \
+          $(srcdir)/src/ml_form_text.h \
+          $(srcdir)/src/ml_form_textarea.h \
+          $(srcdir)/src/ml_heading.h \
+          $(srcdir)/src/ml_horizontal_layout.h \
+          $(srcdir)/src/ml_iframe.h \
+          $(srcdir)/src/ml_image.h \
+          $(srcdir)/src/ml_label.h \
+          $(srcdir)/src/ml_menu.h \
+          $(srcdir)/src/ml_multicol_layout.h \
+          $(srcdir)/src/ml_select_layout.h \
+          $(srcdir)/src/ml_table_layout.h \
+          $(srcdir)/src/ml_text_label.h \
+          $(srcdir)/src/ml_toggle_button.h \
+          $(srcdir)/src/ml_vertical_layout.h \
+          $(srcdir)/src/ml_widget.h \
+          $(srcdir)/src/ml_window.h
+
+WIDGETS_LOBJS := widgets/ml_bulletins.lo \
+       widgets/ml_login_nopw.lo \
+       widgets/ml_msp.lo \
+       widgets/ml_user_directory.lo
+
+WIDGETS_HEADERS := $(srcdir)/widgets/ml_bulletins.h \
+                  $(srcdir)/widgets/ml_login_nopw.h \
+                  $(srcdir)/widgets/ml_msp.h \
+                  $(srcdir)/widgets/ml_user_directory.h
+
+APPS   := apps/msp.so apps/stats.so
+
+EXAMPLES := examples/01_label_and_button.so examples/02_toy_calculator.so \
+       examples/03_many_toy_calculators.so \
+       examples/04_animal_vegetable_mineral.so \
+       examples/05_popup_windows_and_frames.so \
+       examples/06_big_form.so examples/07_toggle_buttons.so \
+       examples/08_menus.so
+#      examples/expenses.so
+
+CALENDAR_LOBJS := calendar/ml_calendar.lo \
+       calendar/ml_calendar_day.lo \
+       calendar/ml_calendar_lib.lo \
+       calendar/ml_calendar_month.lo \
+       calendar/ml_calendar_notes.lo \
+       calendar/ml_calendar_visible.lo
+
+CALENDAR_HEADERS := $(srcdir)/calendar/ml_calendar.h
+
+CHAT_LOBJS := chat/chatroom.lo \
+       chat/lib.lo \
+       chat/message.lo \
+       chat/messages_pane.lo \
+       chat/ml_chat_button.lo \
+       chat/ml_chat_window.lo \
+       chat/thread.lo \
+       chat/users_pane.lo
+
+CHAT_HEADERS := $(srcdir)/chat/ml_chat_button.h \
+       $(srcdir)/chat/ml_chat_window.h
+
+CHAT_BOTS := $(wildcard $(srcdir)/chat/bots/*.pl)
+
+DISCUSSION_LOBJS := discussion/ml_discussion.lo \
+       discussion/ml_discussion_panel.lo
+
+DISCUSSION_HEADERS := $(srcdir)/discussion/ml_discussion.h \
+       $(srcdir)/discussion/ml_discussion_panel.h
+
+all:   build
+
+# XXX Test for flex.
+configure:
+       $(MP_CONFIGURE_START)
+       $(MP_CHECK_LIB) precomp c2lib
+       $(MP_CHECK_LIB) current_pth pthrlib
+       $(MP_CHECK_LIB) new_rws_request rws
+       $(MP_CHECK_FUNCS) dladdr
+       $(MP_CHECK_HEADERS) arpa/inet.h assert.h dlfcn.h fcntl.h \
+       netinet/in.h string.h sys/socket.h sys/types.h time.h unistd.h
+       $(MP_CONFIGURE_END)
+
+build: src/libmonolithcore.so widgets/libmonolithwidgets.so \
+       $(APPS) $(EXAMPLES) \
+       calendar/libmonolithcalendar.so \
+       chat/libmonolithchat.so \
+       discussion/libmonolithdiscussion.so \
+       manpages syms
+#      chat/chatbot.so \
+
+# Build the core library.
+
+src/libmonolithcore.so: $(LOBJS)
+       $(MP_LINK_DYNAMIC) $@ $^ $(LIBS)
+
+src/ml_smarttext.c: src/ml_smarttext.l
+       flex -B -8 -Cem -Pml_smarttext_ -i $<
+       mv lex.ml_smarttext_.c $@
+
+# Build the widgets library.
+
+widgets/libmonolithwidgets.so: $(WIDGETS_LOBJS)
+       $(MP_LINK_DYNAMIC) $@ $^ $(LIBS)
+
+# Build the applications.
+
+apps/%.so: apps/%.lo
+ifneq ($(shell uname), SunOS)
+       $(CC) $(CFLAGS) -shared -Wl,-soname,$@ $^ \
+       -Lwidgets -lmonolithwidgets -Lsrc -lmonolithcore $(LIBS) -o $@
+else
+# XXX make+ needs to support this.
+       $(CC) $(CFLAGS) -shared -Wl,-h,$@ $^ \
+       -Lwidgets -lmonolithwidgets -Lsrc -lmonolithcore $(LIBS) -o $@
+endif
+
+# Build the example programs.
+
+examples/03_many_toy_calculators.so: examples/03_many_toy_calculators.lo \
+       examples/toy_calculator.lo
+ifneq ($(shell uname), SunOS)
+       $(CC) $(CFLAGS) -shared -Wl,-soname,$@ $^ \
+       -Lwidgets -lmonolithwidgets -Lsrc -lmonolithcore $(LIBS) -o $@
+else
+# XXX make+ needs to support this.
+       $(CC) $(CFLAGS) -shared -Wl,-h,$@ $^ \
+       -Lwidgets -lmonolithwidgets -Lsrc -lmonolithcore $(LIBS) -o $@
+endif
+
+examples/expenses.so: examples/expenses.lo examples/expenses_widget.lo
+ifneq ($(shell uname), SunOS)
+       $(CC) $(CFLAGS) -shared -Wl,-soname,$@ $^ \
+       -Lwidgets -lmonolithwidgets -Lsrc -lmonolithcore $(LIBS) -o $@
+else
+# XXX make+ needs to support this.
+       $(CC) $(CFLAGS) -shared -Wl,-h,$@ $^ \
+       -Lwidgets -lmonolithwidgets -Lsrc -lmonolithcore $(LIBS) -o $@
+endif
+
+ifeq ($(shell uname), OpenBSD)
+# .. This is required for unknown reasons by OpenBSD.
+examples/04_animal_vegetable_mineral.so: \
+       examples/04_animal_vegetable_mineral.lo
+       $(CC) $(CFLAGS) -shared $^ \
+       -Lwidgets -lmonolithwidgets -Lsrc -lmonolithcore $(LIBS) -o $@
+endif
+
+examples/%.so: examples/%.lo
+ifneq ($(shell uname), SunOS)
+       $(CC) $(CFLAGS) -shared -Wl,-soname,$@ $^ \
+       -Lwidgets -lmonolithwidgets -Lsrc -lmonolithcore $(LIBS) -o $@
+else
+# XXX make+ needs to support this.
+       $(CC) $(CFLAGS) -shared -Wl,-h,$@ $^ \
+       -Lwidgets -lmonolithwidgets -Lsrc -lmonolithcore $(LIBS) -o $@
+endif
+
+# Build the calendar.
+
+calendar/libmonolithcalendar.so: $(CALENDAR_LOBJS)
+       $(MP_LINK_DYNAMIC) $@ $^ -Lwidgets -lmonolithwidgets \
+       -Lsrc -lmonolithcore $(LIBS)
+
+# Build the chat server.
+
+chat/libmonolithchat.so: $(CHAT_LOBJS)
+       $(MP_LINK_DYNAMIC) $@ $^ -Lwidgets -lmonolithwidgets \
+       -Lsrc -lmonolithcore $(LIBS)
+
+chat/chatbot.so: chat/chatbot.lo
+ifneq ($(shell uname), SunOS)
+       $(CC) $(CFLAGS) -shared -Wl,-soname,$@ $^ $(LIBS) -o $@
+else
+# XXX make+ should provide a way to do this
+       $(CC) $(CFLAGS) -shared -Wl,-h,$@ $^ $(LIBS) -o $@
+endif
+
+
+# Build the discussion system.
+
+discussion/libmonolithdiscussion.so: $(DISCUSSION_LOBJS)
+       $(MP_LINK_DYNAMIC) $@ $^ -Lwidgets -lmonolithwidgets \
+       -Lsrc -lmonolithcore $(LIBS)
+
+# Build all the manpages.
+
+manpages: $(HEADERS) $(WIDGETS_HEADERS) $(CALENDAR_HEADERS) \
+         $(CHAT_HEADERS) $(DISCUSSION_HEADERS)
+       if cdoc; then \
+               rm -f *.3; \
+               cdoc \
+                       --author '$(AUTHOR)' \
+                       --license '$(COPYRIGHT)' \
+                       --version '$(PACKAGE)-$(VERSION)' \
+                       $^; \
+       fi
+
+# Build all the symbol tables.
+
+syms:  src/libmonolithcore.syms widgets/libmonolithwidgets.syms \
+       apps_syms examples_syms
+
+src/libmonolithcore.syms: src/libmonolithcore.so
+       nm $< | sort | grep -i '^[0-9a-f]' | awk '{print $$1 " " $$3}' > $@
+
+widgets/libmonolithwidgets.syms: widgets/libmonolithwidgets.so
+       nm $< | sort | grep -i '^[0-9a-f]' | awk '{print $$1 " " $$3}' > $@
+
+apps_syms: $(patsubst %.so,%.syms,$(APPS))
+
+apps/%.syms: apps/%.so
+       nm $< | sort | grep -i '^[0-9a-f]' | awk '{print $$1 " " $$3}' > $@
+
+examples_syms: $(patsubst %.so,%.syms,$(EXAMPLES))
+
+examples/%.syms: examples/%.so
+       nm $< | sort | grep -i '^[0-9a-f]' | awk '{print $$1 " " $$3}' > $@
+
+test:
+
+install:
+       install -d $(DESTDIR)$(libdir)
+       install -d $(DESTDIR)$(includedir)
+       install -d $(DESTDIR)$(iconsdir)
+       install -d $(DESTDIR)$(man3dir)
+       install -d $(DESTDIR)$(solibdir)
+       install -d $(DESTDIR)$(sqldir)
+       install -d $(DESTDIR)$(styledir)
+       install -d $(DESTDIR)$(symtabsdir)
+       install -d $(DESTDIR)$(botsdir)
+
+       $(MP_INSTALL_DYNAMIC_LIB) src/libmonolithcore.so
+       $(MP_INSTALL_DYNAMIC_LIB) widgets/libmonolithwidgets.so
+       $(MP_INSTALL_DYNAMIC_LIB) calendar/libmonolithcalendar.so
+       $(MP_INSTALL_DYNAMIC_LIB) chat/libmonolithchat.so
+       $(MP_INSTALL_DYNAMIC_LIB) discussion/libmonolithdiscussion.so
+       install -m 0644 $(HEADERS) \
+         $(WIDGETS_HEADERS) \
+         $(CALENDAR_HEADERS) \
+         $(CHAT_HEADERS) \
+         $(DISCUSSION_HEADERS) \
+         $(DESTDIR)$(includedir)
+       install -m 0644 $(srcdir)/icons/*.png $(DESTDIR)$(iconsdir)
+       install -m 0644 *.3 $(DESTDIR)$(man3dir)
+       install -m 0755 $(APPS) $(EXAMPLES) $(DESTDIR)$(solibdir)
+#      install -m 0755 chatbot.so $(DESTDIR)$(solibdir)
+       install -m 0644 $(srcdir)/sql/*.sql $(DESTDIR)$(sqldir)
+       install -m 0644 $(srcdir)/default.css $(DESTDIR)$(styledir)
+       install -m 0644 */*.syms $(DESTDIR)$(symtabsdir)
+       install -m 0755 $(CHAT_BOTS) $(DESTDIR)$(botsdir)
+
+define WEBSITE
+<% include page_header.msp %>
+    <h1>$(PACKAGE) - $(SUMMARY)</h1>
+
+    <p>
+      Monolith is a framework for web applications. Instead of
+      thinking of your web application as a series of pages and forms,
+      Monolith gives you basic widgets like buttons, images, form
+      fields, tables and so on, which you can use to build up your web
+      application or construct reusable <q>super-widgets</q>. In this
+      way, building a web application is rather like constructing a
+      traditional GUI application using Gtk, Motif, Windows/MFC,
+      Java/JFC, etc.
+    </p>
+
+    <p>
+      Monolith applications are written in C or C++ (we may
+      support other languages in future, particularly Perl and Java).
+    </p>
+
+    <p>
+      Monolith applications compile down to standalone shared
+      object scripts which can be run directly from the
+      <a href="../rws/">rws micro web server</a>.
+    </p>
+
+    <p>
+      Monolith comes with a full-featured discussion system, chat
+      server and calendar.
+    </p>
+
+    <p>
+      <a href="doc/">There is extensive documentation and
+       a tutorial here.</a>
+    </p>
+
+    <h1>Download</h1>
+
+    <p>
+      You will need to install the complete tool-chain before
+      installing monolith:
+    </p>
+
+    <ul>
+      <li> <a href="../c2lib/">c2lib</a> - a Perl/STL-like
+       library of basics for C
+      <li> <a href="../pthrlib/">pthrlib</a> - a library for
+       writing small, fast and efficient servers in C
+      <li> <a href="../rws/">rws</a> - a small, fast webserver
+    </ul>
+
+    <p>
+      Always make sure you have the latest versions installed!
+    </p>
+
+    <table border="1">
+      <tr>
+       <th> File </th>
+       <th> Format </th>
+       <th> Contents </th>
+      </tr>
+      <tr>
+       <td> <a href="$(PACKAGE)-$(VERSION).tar.gz">$(PACKAGE)-$(VERSION).tar.gz</a> </td>
+       <td> tar.gz </td>
+       <td> Latest source distribution </td>
+      </tr>
+      <tr>
+       <td> <a href="$(PACKAGE)-$(VERSION)-1.i686.rpm">$(PACKAGE)-$(VERSION)-1.i686.rpm</a> </td>
+       <td> i686 binary RPM </td>
+       <td> Binary libraries, header files, man pages
+         for Red Hat Linux </td>
+      </tr>
+      <tr>
+       <td> <a href="$(PACKAGE)-$(VERSION)-1.src.rpm">$(PACKAGE)-$(VERSION)-1.src.rpm</a> </td>
+       <td> source RPM </td>
+       <td> Source files for Red Hat Linux </td>
+      </tr>
+    </table>
+
+    <p>
+      Note: To rebuild the manual pages which come with the
+      package, you will also need to download
+      <a href="../c2lib/">cdoc &gt;= 0.9.2</a> (this is
+      optional).
+    </p>
+
+    <h2>News</h2>
+
+<p>
+<b>Sat Feb  8 17:00:47 GMT 2003:</b>
+Ported to Solaris, OpenBSD and FreeBSD (thanks to
+<a href="http://www.azazel.net/">Jeremy Sowden</a>
+and <a href="http://www.mondaymorning.org/">Richard Baker</a>
+for help and equipment).
+Removed all non-shared libraries. Allow uses to
+set <code>class</code> on <code>div</code> elements.
+Fixed many string escaping bugs. Fixed spelling mistakes
+in the documentation. Updated CSS for the bugtracker.
+</p>
+
+       <p>
+       <b>Sun Dec  8 17:23:47 GMT 2002:</b>
+       Builds using <a href="../makeplus/">make+</a>.
+       Fixes to compile on RH 7.3. Changed to support
+       <code>current_pth</code> API change in
+       <code>pthrlib</code>.
+       <code>ml_login_nopw</code> acts more conservatively
+       with email addresses, possibly closing a security
+       hole. Added <code>ml_select_layout</code>. Made
+       the button widget more memory efficient.
+       Allow widgets to widget properties. Changes to
+       the stylesheet for discussion. Enabled debugging
+       and optimisations. <code>ml_window</code> supports
+       browser refresh (primarily for chat). Added two
+       forgotten <code>fclose</code> calls in the stats
+       code. Added <code>ml_menu</code> - initial support
+       for pull-down menus. Integrated smart text into the
+       core library. User agent now stored in the session,
+       and shown in stats.
+       </p>
+
+       <p>
+       <b>Mon Nov 25 09:31:37 GMT 2002:</b>
+       Added support for smart text, and updated widgets to
+       support it.
+       Better handling of session timeouts.
+       Fixed <code>kill_session</code> to be race-free.
+       Stats application now suports hit counts for sessions.
+       Stats application can do full symbol resolution.
+       Forms have names. Form text input fields can capture
+       focus.
+       </p>
+
+       <p>
+       <b>Sun Nov 17 23:31:32 GMT 2002:</b>
+       Debian packages. Added MSP files. Simplified the look of
+       the <code>ml_login_nopw</code> widget. Added the <code>icons</code>
+       directory. Added <code>begin work ... commit work</code>
+       around SQL scripts. Install SQL in <code>/usr/share/rws/sql/</code>
+       directory. Install so scripts in <code>/usr/share/rws/so-bin/</code>.
+       Added widget properties. Connection pooling completely
+       rewritten and simplified. Added <code>ml_die</code> function.
+       Added the ability to get the peername. Added <code>ml_heading</code>
+       widget. Added CSS for commercial widgets. <code>ml_msp</code>
+       now supports up to 5 arguments to the widget function (for chat).
+       Close button allows opener window to be reloaded.
+       Added tabbed layout widget (not working yet).
+       Added <code>ml_form_layout</code> layout widget.
+       Added <code>ml_user_directory</code> widget for bug tracker
+       (not working yet). Added iframe widget. Support
+       scroll_to for windows. Colours, tooltips, link types
+       for buttons. Added more styles. Added styling for link
+       buttons. Added <code>class</code> property for many of
+       the widgets. Added close buttons, bulletins board widget.
+       Multi-column, horizontal and vertical layout widgets.
+       Added <code>color</code> property for text label widgets.
+       Session structures are now properly locked to avoid
+       potential data corruption.
+       </p>
+
+    <p>
+      <b>Thu Nov 14 15:33:29 GMT 2002:</b> Major checkpoint release
+      for Monolith.
+    </p>
+
+    <p>
+      <b>Sun Oct 20 15:04:07 BST 2002:</b> Correct flags for
+      PCRE. Many bug fixes to MSPs. User authentication.
+      MSPs can now access the
+      database. <code>ml_bulletins</code> allows you to post
+      message, supports user authentication. Added a
+      <code>ml_close_button</code> widget to core. Dialogs
+      now support close buttons. Added <code>ml_ok_window</code>
+      and <code>ml_error_window</code>. Allow forms to change
+      their posting method. <code>ml_text_label</code> doesn't
+      crash if there is no text. <code>ml_login_nopw</code> widget.
+      Get the window ID of a window (required by the login widget).
+      Nominate a "default" main window (required by MSPs).
+      Provided a way to get at the original Host: header in
+      the request. Fixed multiple bugs in persistent database
+      handles.
+      This version now requires a version of pthrlib which was
+      compiled with DBI support.
+    </p>
+
+    <p>
+      <b>Tue Oct 15 23:40:42 BST 2002:</b> Multiple bug fixes.
+    </p>
+
+    <p>
+      <b>Sat Sep  7 16:01:14 BST 2002:</b> Packages are now
+      available as i686 binary RPMs and source RPMs.
+    </p>
+
+    <h2>Old news and old versions</h2>
+
+    <p>
+      <b>Sat Sep  7 14:48:24 BST 2002</b>
+    </p>
+
+    <p>
+      <a href="monolith-1.0.1.tar.gz">monolith-1.0.1.tar.gz</a> released.
+      Documentation and tutorial. Fixed missing includes of
+      <string.h> (thanks to jjr-at-japo-dot-fi)
+    </p>
+
+    <p>
+      <b>Fri Aug 30 15:28:57 BST 2002</b>
+    </p>
+
+    <p>
+      <a href="monolith-1.0.0.tar.gz">monolith-1.0.0.tar.gz</a> released.
+      Full complement of form input widgets created.
+    </p>
+
+    <p>
+      <b>Wed Aug 28 20:30:33 BST 2002</b>
+    </p>
+
+    <p>
+      <a href="monolith-0.8.6.tar.gz">monolith-0.8.6.tar.gz</a> released.
+      This is a candidate for release as version 1.0.
+      Added framesets, removed private data in session, removed
+      explicit name parameters on form input fields.
+    </p>
+
+    <p>
+      <b>Wed Aug 28 16:11:09 BST 2002</b>
+    </p>
+
+    <p>
+      <a href="monolith-0.8.5.tar.gz">monolith-0.8.5.tar.gz</a> released.
+      More details in the README file on how to install monolith.
+    </p>
+
+    <p>
+      <b>Wed Aug 28 15:41:50 BST 2002</b>
+    </p>
+
+    <p>
+      <a href="monolith-0.8.4.tar.gz">monolith-0.8.4.tar.gz</a> released.
+      Support for multiple windows, improved CSS, improved support for
+      forms.
+    </p>
+
+    <p>
+      <b>Tue Aug 27 11:17:00 BST 2002</b>
+    </p>
+
+    <p>
+      <a href="monolith-0.8.3.tar.gz">monolith-0.8.3.tar.gz</a> released.
+      This version has support for forms, and more example programs.
+    </p>
+
+    <p>
+      <b>Sat Aug 24 16:55:21 BST 2002</b>
+    </p>
+
+    <p>
+      <a href="monolith-0.8.2.tar.gz">monolith-0.8.2.tar.gz</a> released.
+      This fixes some bugs in the button widget, finishes off the
+      <q>toy calculator</q> example, and improves the box widget.
+    </p>
+
+    <p>
+      <b>Sat Aug 24 15:13:58 BST 2002</b>
+    </p>
+
+    <p>
+      <a href="monolith-0.8.1.tar.gz">monolith-0.8.1.tar.gz</a> released.
+      This release is slightly more substantial than the previous one.
+      It includes a few more widgets, and CSS changes to make the
+      example programs much nicer-looking.
+    </p>
+
+    <p>
+      <b>Fri Aug 23 15:00:51 BST 2002</b>
+    </p>
+
+    <p>
+      <a href="monolith-0.8.0.tar.gz">monolith-0.8.0.tar.gz</a> released.
+      This is the first basic release of the new C/C++-based monolith
+      library. It contains a single simple example program and just a
+      few basic widgets.
+    </p>
+
+<% include page_footer.msp %>
+endef
+
+upload_website:
+       scp $(PACKAGE)-$(VERSION).tar.gz $(PACKAGE)-$(VERSION)-1.*.rpm \
+       $(PACKAGE)-$(VERSION).bin.tar.gz \
+       10.0.0.248:annexia.org/freeware/$(PACKAGE)/
+       scp index.html \
+       10.0.0.248:annexia.org/freeware/$(PACKAGE)/index.msp
+
+.PHONY:        build configure test upload_website
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..b0c310b
--- /dev/null
+++ b/README
@@ -0,0 +1,91 @@
+Monolith (C) Copyright 2000-2002 Richard W.M. Jones <rich@annexia.org>
+
+Monolith is distributed under the terms of the GNU Library GPL. See
+the accompanying file COPYING.LIB for details.
+
+Please see doc/index.html for full documentation and a tutorial.
+
+Installing Monolith
+-------------------
+
+You first need to install the following components, in the order
+listed below:
+
+0. cdoc    from http://www.annexia.org/freeware/c2lib/ (optional)
+1. c2lib   from http://www.annexia.org/freeware/c2lib/
+2. pthrlib from http://www.annexia.org/freeware/pthrlib/
+3. rws     from http://www.annexia.org/freeware/rws/
+
+Make sure you have the latest versions of these packages. Older
+versions may not work correctly.
+
+Build it:
+
+       ./configure --sysconfdir=/etc
+       make+
+       make+ test
+
+Install it (as root):
+
+       make+ install
+
+You will now need to configure rws. The configuration files for rws
+are normally located in the /etc/rws/ directory.
+
+For the /etc/rws/rws.conf file, just use the example one supplied with
+rws. You can modify it if you want, but I didn't.
+
+Now create a /etc/rws/hosts/localhost file which should contain:
+
+       alias /
+               # Path to the document root. NOTE you will
+               # definitely want to change this!
+               path:           /var/www
+               show:           1
+               list:           1
+       end alias
+
+       alias /so-bin/
+               path:           /usr/local/share/rws/so-bin
+               exec so:        1
+       end alias
+
+       alias /ml-styles/
+               path:           /usr/local/share/rws/ml-styles
+               show:           1
+       end alias
+
+       alias /ml-icons/
+               path:           /usr/local/share/rws/ml-icons
+               show:           1
+       end alias
+
+These are the default locations for the files if you installed
+Monolith with --prefix=/usr/local, but you may need to change these if
+you installed with another prefix.
+
+Create symbolic links as necessary to your localhost file:
+
+       cd /etc/rws/hosts
+       ln -s localhost:8080 localhost
+       ln -s www.example.com localhost
+
+etc.
+
+Now, start up rws (as root, of course):
+
+       /usr/local/sbin/rwsd -C /etc/rws -p 8080
+
+Fire up a browser and point it at
+
+       http://localhost:8080/so-bin/01_label_and_button.so
+
+Errors will be displayed in the error log (/tmp/error_log,
+/var/log/httpd/error_log or /var/log/rws/error_log).
+
+If the button doesn't appear as a white square surrounded by a black
+rectangle, then the stylesheet is not being found. Make sure you set
+up the /ml-styles/ alias correctly.
+
+Monolith installs other example programs. Look in the examples/
+directory for a full list.
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..9f4cc1e
--- /dev/null
+++ b/TODO
@@ -0,0 +1,33 @@
+Known bugs:
+
+* Fix bug with updating value fields on submit buttons.
+
+Desirable widgets and features:
+
+* Radios built from toggle buttons.
+
+* Tabs and tabbed panes.
+  
+* True links (looking like buttons or URLs?) [note: can be emulated now
+  using ml_button and redirects -- perhaps an ml_link_button class to
+  wrap this up?]
+  
+* Menubar, menus, menu items.
+
+* File open (this will have to have a fairly flexible VFS layer since
+  it obviously can't access local disk)
+
+* Iframe or object (?)
+
+* Fixed placement layout exposing more positioning features of CSS2
+
+* Popup menus (if these are possible)
+
+* Themes
+
+* Buttons should have: [IMG] Text (with IMG being optional) so that
+  we can style them differently if desired.
+
+* User authentication in a more integrated manner.
+
+* Update documentation to cover recent changes to the API
\ No newline at end of file
diff --git a/apps/README b/apps/README
new file mode 100644 (file)
index 0000000..32cb6e2
--- /dev/null
@@ -0,0 +1,50 @@
+monolith/apps directory
+-----------------------
+
+msp - monolith server pages
+---------------------------
+
+MSPs (".msp" files) are HTML files with additional mark-up. The main
+benefit is that they allow you to embed working monolith widgets into
+existing HTML pages.
+
+The current implementation of MSPs should be regarded as very much
+"version 0.1". We will probably rewrite it from scratch when we have
+time to use something which looks a lot more like XML. However, the
+current version is usable; indeed http://www.annexia.org/ is built
+using MSPs.
+
+The msp app contained in this directory is just a wrapper around the
+ml_msp widget which can be found in the ../widgets/ directory. For
+full documentation on writing MSP files, look at the new_ml_msp(3)
+manual page.
+
+To use MSPs, add the following to your /etc/rws/hosts/<hostname> file:
+
+begin rewrite
+^/(.*\.msp)$    /so-bin/msp.so?page=$1  last,qsa
+^/$             /index.msp              qsa,external
+^/(.*)/$        /$1/index.msp           qsa,external
+end rewrite
+
+msp root:       <document root directory>
+msp database:   dbname=<name of the database>
+
+stats - monolith statistics/debugging application
+-------------------------------------------------
+
+The stats interface allows you to browse around the internals of a
+running rws/monolith server, displaying structures and updating them
+in real time. It can be quite useful for debugging.
+
+For security reasons, this application does not install itself by
+default. You need to manually copy the stats.so file to
+/usr/share/rws/so-bin, and make sure it is mode 0755. You should also
+consider NOT installing this on live servers - at least until rws
+supports access control. This is because the sensitive internal
+information revealed by stats might make it easier for a cracker to
+break into your system.
+
+To run the stats application, point your browser at
+http://yourhostname/so-bin/stats.so . It should be self-explanatory
+from there on.
diff --git a/apps/msp.c b/apps/msp.c
new file mode 100644 (file)
index 0000000..34c2675
--- /dev/null
@@ -0,0 +1,107 @@
+/* Monolith server-parsed pages (.msp's).
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: msp.c,v 1.8 2003/02/22 15:34:24 rich Exp $
+ */
+
+#include <pool.h>
+#include <pstring.h>
+
+#include <pthr_cgi.h>
+
+#include "monolith.h"
+#include "ml_window.h"
+#include "ml_msp.h"
+
+/*----- The following standard boilerplate code must appear -----*/
+
+/* Main entry point to the app. */
+static void app_main (ml_session);
+
+int
+handle_request (rws_request rq)
+{
+  return ml_entry_point (rq, app_main);
+}
+
+/*----- End of standard boilerplate code -----*/
+
+static void
+app_main (ml_session session)
+{
+  pool pool = ml_session_pool (session);
+  ml_window w;
+  ml_msp msp;
+  const char *conninfo, *rootdir, *filename, *canonical_path;
+  cgi args;
+
+  canonical_path = ml_session_canonical_path (session);
+
+  /* Create the top-level window. */
+  w = new_ml_window (session, pool);
+
+  /* Disable the window headers. We can assume that the .msp file will
+   * supply <html>...</html>.
+   */
+  ml_window_set_headers_flag (w, 0);
+
+  /* Nominate this window as the main window for this application. */
+  ml_session_set_main_window (session, w);
+
+  /* Get the document root. */
+  rootdir = ml_cfg_get_string (session, "msp root", 0);
+  if (rootdir == 0)
+    {
+      fprintf (stderr, "%s: no \"msp root\" directive in conf file.\n",
+              canonical_path);
+      return;
+    }
+
+  /* Get the database connection name. */
+  conninfo = ml_cfg_get_string (session, "msp database", 0);
+
+  /* Get the filename. */
+  args = ml_session_args (session);
+  if (!args || (filename = cgi_param (args, "page")) == 0)
+    {
+      fprintf (stderr, "%s: missing page parameter.\n", canonical_path);
+      return;
+    }
+
+  /* Create the msp widget. */
+  msp = new_ml_msp (pool, session, conninfo, rootdir, filename);
+  if (!msp)
+    {
+      const char *msg =
+       psprintf (pool,
+                 "%s: failed to create msp widget (bad filename "
+                 "or msp file contains errors?)",
+                 canonical_path);
+
+      /* Send some extra debug to the log file. */
+      fprintf (stderr,
+              "%s\n"
+              "%s: rootdir = %s, filename = %s\n",
+              msg, canonical_path, rootdir, filename);
+
+      /* Die with an error back to the browser. */
+      pth_die (msg);
+    }
+
+  /* Pack the msp widget into the window. */
+  ml_window_pack (w, msp);
+}
diff --git a/apps/stats.c b/apps/stats.c
new file mode 100644 (file)
index 0000000..f1bd2ac
--- /dev/null
@@ -0,0 +1,797 @@
+/* Monolith statistics/debugging application.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: stats.c,v 1.10 2003/02/22 15:34:24 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_cgi.h>
+#include <pthr_dbi.h>
+
+#include "monolith.h"
+#include "ml_window.h"
+#include "ml_widget.h"
+#include "ml_form_layout.h"
+#include "ml_table_layout.h"
+#include "ml_multicol_layout.h"
+#include "ml_flow_layout.h"
+#include "ml_form.h"
+#include "ml_form_select.h"
+#include "ml_form_submit.h"
+#include "ml_text_label.h"
+#include "ml_button.h"
+#include "ml_dialog.h"
+
+/*----- The following standard boilerplate code must appear -----*/
+
+/* Main entry point to the app. */
+static void app_main (ml_session);
+
+int
+handle_request (rws_request rq)
+{
+  return ml_entry_point (rq, app_main);
+}
+
+/*----- End of standard boilerplate code -----*/
+
+/* Per-session data structure. */
+struct data
+{
+  pool pool;                   /* Session pool for allocations. */
+
+  /* This is the top-level window. */
+  ml_window std_win;
+  ml_table_layout std_tbl;
+  ml_form std_form;            /* Area selection form. */
+  ml_flow_layout std_flow;
+  ml_form_select std_choices;  /* Top-level choices. */
+  ml_form_submit std_submit;
+};
+
+/* Arguments to the show_session function. */
+struct show_session_args
+{
+  const char *sessionid;
+  struct data *data;
+};
+
+static void show_reactor (ml_session session, struct data *data);
+static void list_sessions (ml_session session, struct data *data);
+static void show_session (ml_session session, void *vargs);
+static void list_threads (ml_session session, struct data *data);
+static void jump_to (ml_session session, void *vdata);
+static void pack (struct data *data, ml_widget widget);
+static const char *resolve_addr (pool pool, unsigned long addr);
+static const char *pr_time (pool pool, reactor_time_t time);
+
+static struct {
+  const char *choice;
+  void (*fn) (ml_session, struct data *data);
+  int is_default;
+} choices[] = {
+  { "Reactor",                   show_reactor,  0 },
+  { "Sessions",                  list_sessions, 1 },
+  { "Threads",                   list_threads,  0 },
+};
+
+#define nr_choices (sizeof choices / sizeof choices[0])
+
+/* If this variable is set, then sessionids are not revealed in full. Set
+ * this using the 'monolith stats restricted: 1' flag in the configuration
+ * file.
+ */
+static int restricted = 1;
+
+/* If this variable is set, then the application is available to everyone.
+ * If not set, then it is only available to localhost. Set this using the
+ * 'monolith stats world readable: 1' flag in the configuration file.
+ */
+static int world_readable = 0;
+
+static void
+app_main (ml_session session)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data;
+  int i;
+
+  /* Read configuration settings. */
+  restricted = ml_cfg_get_bool (session, "monolith stats restricted", 1);
+  world_readable =
+    ml_cfg_get_bool (session, "monolith stats world readable", 0);
+
+  /* Allocate per-session data structure. */
+  data = pmalloc (pool, sizeof *data);
+  data->pool = pool;
+
+  /* Lay out the window. */
+  data->std_win = new_ml_window (session, pool);
+  data->std_tbl = new_ml_table_layout (pool, 2, 1);
+  data->std_form = new_ml_form (pool);
+  data->std_flow = new_ml_flow_layout (pool);
+  data->std_choices = new_ml_form_select (pool, data->std_form);
+  ml_flow_layout_pack (data->std_flow, data->std_choices);
+  data->std_submit = new_ml_form_submit (pool, data->std_form, "Go");
+  ml_flow_layout_pack (data->std_flow, data->std_submit);
+  ml_form_pack (data->std_form, data->std_flow);
+  ml_table_layout_pack (data->std_tbl, data->std_form, 0, 0);
+  ml_window_pack (data->std_win, data->std_tbl);
+
+  /* Populate the list of choices. */
+  for (i = 0; i < nr_choices; ++i)
+    {
+      ml_form_select_push_back (data->std_choices, choices[i].choice);
+      if (choices[i].is_default)
+       ml_form_select_set_selection (data->std_choices, i);
+    }
+
+  /* Set the form callback so that we jump to the right page. */
+  ml_form_set_callback (data->std_form, jump_to, session, data);
+
+  /* Set the window title. */
+  ml_window_set_title (data->std_win,
+                      "Monolith statistics and debugging application");
+
+  /* Jump to the initial page (default choice). */
+  jump_to (session, data);
+}
+
+static void
+jump_to (ml_session session, void *vdata)
+{
+  struct data *data = (struct data *) vdata;
+  int i;
+
+  /* Find out which choice is selected. */
+  i = ml_form_select_get_selection (data->std_choices);
+  if (i >= 0 && i < nr_choices)
+    /* Run that function to update the page. */
+    choices[i].fn (session, data);
+}
+
+static void
+pack (struct data *data, ml_widget widget)
+{
+  ml_table_layout_pack (data->std_tbl, widget, 1, 0);
+}
+
+static const char *
+disguise_sessionid (pool pool, const char *sessionid)
+{
+  if (!restricted) return sessionid;
+  else
+    {
+      char *s = pstrndup (pool, sessionid, 6);
+      s = pstrcat (pool, s, "[...]");
+      return s;
+    }
+}
+
+static void
+show_reactor (ml_session session, struct data *data)
+{
+  /* XXX not impl. */
+}
+
+static void
+list_sessions (ml_session session, struct data *data)
+{
+  pool pool = data->pool;
+  vector sessionids;
+  ml_multicol_layout tbl;
+  ml_text_label lbl;
+  int i;
+
+  /* Pull out the list of session IDs and turn them into session objects. */
+  sessionids = _ml_get_sessions (pool);
+
+  tbl = new_ml_multicol_layout (pool, 5);
+  ml_widget_set_property (tbl, "class", "ml_stats_table");
+
+  /* Table headers. */
+  lbl = new_ml_text_label (pool, "sessionid");
+  ml_multicol_layout_set_header (tbl, 1);
+  ml_multicol_layout_pack (tbl, lbl);
+  lbl = new_ml_text_label (pool, "last access");
+  ml_multicol_layout_set_header (tbl, 1);
+  ml_multicol_layout_pack (tbl, lbl);
+  lbl = new_ml_text_label (pool, "address");
+  ml_multicol_layout_set_header (tbl, 1);
+  ml_multicol_layout_pack (tbl, lbl);
+  lbl = new_ml_text_label (pool, "size");
+  ml_multicol_layout_set_header (tbl, 1);
+  ml_multicol_layout_pack (tbl, lbl);
+  lbl = new_ml_text_label (pool, "path");
+  ml_multicol_layout_set_header (tbl, 1);
+  ml_multicol_layout_pack (tbl, lbl);
+
+  /* The sessions themselves. */
+  for (i = 0; i < vector_size (sessionids); ++i)
+    {
+      const char *sessionid;
+      ml_session s;
+      ml_button b;
+      struct sockaddr_in addr;
+      const char *host_header, *canonical_path;
+      struct pool *sp;
+      struct pool_stats pool_stats;
+      struct show_session_args *args;
+
+      vector_get (sessionids, i, sessionid);
+      s = _ml_get_session (sessionid);
+
+      b = new_ml_button (pool, disguise_sessionid (pool, sessionid));
+      ml_widget_set_property (b, "button.style", "link");
+      args = pmalloc (pool, sizeof *args);
+      args->sessionid = sessionid;
+      args->data = data;
+      ml_button_set_callback (b, show_session, session, args);
+      ml_button_set_popup (b, psprintf (pool, "session_%s", sessionid));
+      ml_button_set_popup_size (b, 640, 480);
+      ml_multicol_layout_pack (tbl, b);
+
+      lbl = new_ml_text_label (pool,
+                              pr_time (pool,
+                                       _ml_session_get_last_access (s)));
+      ml_multicol_layout_pack (tbl, lbl);
+
+      addr = _ml_session_get_original_ip (s);
+      lbl = new_ml_text_label (pool, pstrdup (pool,
+                                             inet_ntoa (addr.sin_addr)));
+      ml_multicol_layout_pack (tbl, lbl);
+
+      sp = ml_session_pool (s);
+      pool_get_stats (sp, &pool_stats, sizeof (pool_stats));
+      lbl = new_ml_text_label (pool, pitoa (pool, pool_stats.struct_size));
+      ml_multicol_layout_pack (tbl, lbl);
+
+      host_header = ml_session_host_header (s);
+      canonical_path = ml_session_canonical_path (s);
+      lbl = new_ml_text_label (pool,
+                              psprintf (pool, "http://%s%s",
+                                        host_header, canonical_path));
+      ml_multicol_layout_pack (tbl, lbl);
+    }
+
+  pack (data, tbl);
+}
+
+static void
+show_session (ml_session session, void *vargs)
+{
+  struct show_session_args *args = (struct show_session_args *) vargs;
+  struct data *data = args->data;
+  pool pool = data->pool;
+  const char *sessionid = args->sessionid;
+  ml_window win;
+  ml_form_layout tbl;
+  ml_text_label lbl;
+  ml_session s;
+
+  win = new_ml_window (session, pool);
+  tbl = new_ml_form_layout (pool);
+  ml_widget_set_property (tbl, "class", "ml_stats_table");
+
+  /* Get the session. */
+  s = _ml_get_session (sessionid);
+  if (!s)
+    {
+      ml_error_window (pool, session,
+                      "Session not found. It is likely that the session "
+                      "has timed out and been deleted.",
+                      ML_DIALOG_CLOSE_BUTTON);
+      return;
+    }
+
+  /* Get the fields from the session and display them. */
+
+  ml_form_layout_pack (tbl, "sessionid",
+                      new_ml_text_label (pool,
+                        disguise_sessionid (pool, sessionid)));
+  ml_form_layout_pack (tbl, "path",
+                      new_ml_text_label (pool,
+                        psprintf (pool, "http://%s%s",
+                                  ml_session_host_header (s),
+                                  ml_session_canonical_path (s))));
+
+  /* Pool and pool stats. */
+  {
+    struct pool *sp;
+    struct pool_stats pool_stats;
+
+    sp = ml_session_pool (s);
+    pool_get_stats (sp, &pool_stats, sizeof (pool_stats));
+
+    ml_form_layout_pack (tbl, "session pool",
+                        new_ml_text_label (pool, psprintf (pool, "%p", sp)));
+    ml_form_layout_pack (tbl, "session pool size",
+                        new_ml_text_label (pool,
+                          pitoa (pool, pool_stats.struct_size)));
+  }
+
+  ml_form_layout_pack (tbl, "access count",
+                      new_ml_text_label (pool,
+                        pitoa (pool, _ml_session_get_hits (s))));
+  ml_form_layout_pack (tbl, "last access",
+                      new_ml_text_label (pool,
+                        pr_time (pool, _ml_session_get_last_access (s))));
+  ml_form_layout_pack (tbl, "creation time",
+                      new_ml_text_label (pool,
+                        pr_time (pool, _ml_session_get_created (s))));
+
+  /* Original IP address. */
+  {
+    struct sockaddr_in addr;
+
+    addr = _ml_session_get_original_ip (s);
+    ml_form_layout_pack (tbl, "original ip",
+                        new_ml_text_label (pool,
+                          pstrdup (pool, inet_ntoa (addr.sin_addr))));
+  }
+
+  /* User-Agent header. */
+  {
+    const char *ua = ml_session_user_agent (s);
+
+    ml_form_layout_pack (tbl, "user agent",
+                        new_ml_text_label (pool, ua ? pstrdup (pool, ua)
+                                           : "(not set)"));
+  }
+
+  /* Initial args. */
+  {
+    cgi args;
+    ml_multicol_layout args_tbl;
+    int i;
+    vector keys;
+
+    args = ml_session_args (s);
+    /* XXX cgi_params_in_pool function */
+    keys = cgi_params (args);
+
+    args_tbl = new_ml_multicol_layout (pool, 2);
+    ml_widget_set_property (args_tbl, "class", "ml_stats_table");
+
+    lbl = new_ml_text_label (pool, "name");
+    ml_multicol_layout_set_header (args_tbl, 1);
+    ml_multicol_layout_pack (args_tbl, lbl);
+
+    lbl = new_ml_text_label (pool, "value");
+    ml_multicol_layout_set_header (args_tbl, 1);
+    ml_multicol_layout_pack (args_tbl, lbl);
+
+    for (i = 0; i < vector_size (keys); ++i)
+      {
+       const char *key, *value;
+
+       vector_get (keys, i, key);
+       value = cgi_param (args, key);
+
+       lbl = new_ml_text_label (pool, pstrdup (pool, key));
+       ml_multicol_layout_pack (args_tbl, lbl);
+       lbl = new_ml_text_label (pool, pstrdup (pool, value));
+       ml_multicol_layout_pack (args_tbl, lbl);
+      }
+
+    ml_form_layout_pack (tbl, "initial parameters", args_tbl);
+  }
+
+  ml_form_layout_pack (tbl, "app_main function",
+                      new_ml_text_label (pool,
+                        resolve_addr (pool,
+                          (unsigned long) _ml_session_get_app_main (s))));
+
+  /* Windows. */
+  {
+    vector windows;
+    ml_multicol_layout win_tbl;
+    int i;
+
+    windows = _ml_session_get_windows (s, pool);
+
+    win_tbl = new_ml_multicol_layout (pool, 2);
+    ml_widget_set_property (win_tbl, "class", "ml_stats_table");
+
+    lbl = new_ml_text_label (pool, "windowid");
+    ml_multicol_layout_set_header (win_tbl, 1);
+    ml_multicol_layout_pack (win_tbl, lbl);
+
+    lbl = new_ml_text_label (pool, "window");
+    ml_multicol_layout_set_header (win_tbl, 1);
+    ml_multicol_layout_pack (win_tbl, lbl);
+
+    for (i = 0; i < vector_size (windows); ++i)
+      {
+       const char *windowid;
+       ml_window window;
+
+       vector_get (windows, i, windowid);
+       window = _ml_session_get_window (s, windowid);
+
+       lbl = new_ml_text_label (pool, windowid);
+       ml_multicol_layout_pack (win_tbl, lbl);
+       lbl = new_ml_text_label (pool, psprintf (pool, "%p", window));
+       ml_multicol_layout_pack (win_tbl, lbl);
+      }
+
+    ml_form_layout_pack (tbl, "windows", win_tbl);
+  }
+
+  /* Actions. */
+  {
+    vector actions;
+    ml_multicol_layout act_tbl;
+    int i;
+
+    actions = _ml_session_get_actions (s, pool);
+
+    act_tbl = new_ml_multicol_layout (pool, 3);
+    ml_widget_set_property (act_tbl, "class", "ml_stats_table");
+
+    lbl = new_ml_text_label (pool, "actionid");
+    ml_multicol_layout_set_header (act_tbl, 1);
+    ml_multicol_layout_pack (act_tbl, lbl);
+
+    lbl = new_ml_text_label (pool, "function");
+    ml_multicol_layout_set_header (act_tbl, 1);
+    ml_multicol_layout_pack (act_tbl, lbl);
+
+    lbl = new_ml_text_label (pool, "data");
+    ml_multicol_layout_set_header (act_tbl, 1);
+    ml_multicol_layout_pack (act_tbl, lbl);
+
+    for (i = 0; i < vector_size (actions); ++i)
+      {
+       const char *actionid;
+       void *fn, *data;
+
+       vector_get (actions, i, actionid);
+       _ml_session_get_action (s, actionid, &fn, &data);
+
+       lbl = new_ml_text_label (pool, actionid);
+       ml_multicol_layout_pack (act_tbl, lbl);
+       lbl = new_ml_text_label (pool, resolve_addr (pool,
+                                                      (unsigned long) fn));
+       ml_multicol_layout_pack (act_tbl, lbl);
+       lbl = new_ml_text_label (pool, psprintf (pool, "%p", data));
+       ml_multicol_layout_pack (act_tbl, lbl);
+      }
+
+    ml_form_layout_pack (tbl, "actions", act_tbl);
+  }
+
+  ml_form_layout_pack (tbl, "userid",
+                      new_ml_text_label (pool,
+                        pitoa (pool, ml_session_userid (s))));
+
+  ml_window_pack (win, tbl);
+
+  /* Set window title. */
+  ml_window_set_title (win, psprintf (pool, "Session %s",
+                                     disguise_sessionid (pool, sessionid)));
+}
+
+static void
+list_threads (ml_session session, struct data *data)
+{
+  pool pool = data->pool;
+  vector threads;
+  int i;
+  ml_multicol_layout tbl;
+  ml_text_label lbl;
+
+  /* Get the threads. */
+  threads = pseudothread_get_threads (pool);
+
+  tbl = new_ml_multicol_layout (pool, 4);
+  ml_widget_set_property (tbl, "class", "ml_stats_table");
+
+  /* Table headers. */
+  lbl = new_ml_text_label (pool, "id");
+  ml_multicol_layout_set_header (tbl, 1);
+  ml_multicol_layout_pack (tbl, lbl);
+  lbl = new_ml_text_label (pool, "program counter");
+  ml_multicol_layout_set_header (tbl, 1);
+  ml_multicol_layout_pack (tbl, lbl);
+  lbl = new_ml_text_label (pool, "size");
+  ml_multicol_layout_set_header (tbl, 1);
+  ml_multicol_layout_pack (tbl, lbl);
+  lbl = new_ml_text_label (pool, "name");
+  ml_multicol_layout_set_header (tbl, 1);
+  ml_multicol_layout_pack (tbl, lbl);
+
+  for (i = 0; i < vector_size (threads); ++i)
+    {
+      pseudothread pth;
+      ml_button b;
+      struct pool *tp;
+      struct pool_stats pool_stats;
+      unsigned long pc;
+      const char *pc_symbol;
+      const char *name;
+
+      vector_get_ptr (threads, i, pth);
+
+      lbl = new_ml_text_label (pool, pitoa (pool, pth_get_thread_num (pth)));
+      ml_multicol_layout_pack (tbl, lbl);
+
+      pc = pth_get_PC (pth);
+      pc_symbol = resolve_addr (pool, pc);
+      lbl = new_ml_text_label (pool, pc_symbol);
+      ml_multicol_layout_pack (tbl, lbl);
+
+      tp = pth_get_pool (pth);
+      pool_get_stats (tp, &pool_stats, sizeof (pool_stats));
+      lbl = new_ml_text_label (pool, pitoa (pool, pool_stats.struct_size));
+      ml_multicol_layout_pack (tbl, lbl);
+
+      name = pstrdup (pool, pth_get_name (pth));
+      b = new_ml_button (pool, name);
+      ml_widget_set_property (b, "button.style", "link");
+      ml_multicol_layout_pack (tbl, b);
+    }
+
+  pack (data, tbl);
+}
+
+static const char *
+pr_time (pool pool, reactor_time_t time)
+{
+  int secs = (reactor_time - time) / 1000LL;
+
+  if (secs < 60)
+    return psprintf (pool, "%ds", secs);
+  else if (secs < 3600)
+    return psprintf (pool, "%dm %ds", secs / 60, secs % 60);
+  else
+    return psprintf (pool, "%dh %dm %ds", secs / 3600, (secs / 60) % 60,
+                    secs % 60);
+}
+
+/*----- Address to symbol resolution code. -----*/
+
+/* This code is highly Linux-dependent.
+ *
+ * The strategy taken is as follows:
+ *
+ * (1) Look at /proc/$$/maps (if it exists) to try and work out which
+ *     executable or shared library the symbol exists in.
+ * (2) Look for a corresponding symbol table file in /usr/share/rws/symtabs/
+ * (3) If the symbol table file is found, then we can resolve the symbol
+ *     immediately with that file.
+ * (4) If no symbol table file is found, use the dladdr(3) function from
+ *     glibc.
+ * (5) If there is no dladdr(3) function, just print the address.
+ *
+ * All of this must be done without blocking.
+ */
+
+#define RES_DEBUG 0
+
+static const char *resolve_addr_in_maps (pool, unsigned long);
+static const char *resolve_addr_in_symtab (pool, unsigned long, const char *,
+                                          unsigned long, unsigned long);
+static const char *resolve_addr_with_dladdr (pool, unsigned long);
+
+static const char *
+resolve_addr (pool session_pool, unsigned long addr)
+{
+  pool pool;
+  const char *s;
+
+#if RES_DEBUG
+  fprintf (stderr, "trying to resolve address %lx\n", addr);
+#endif
+
+  /* Give the address resolution code its own scratch pool, but copy
+   * the result into the more permanent storage of the session pool.
+   */
+  pool = new_subpool (session_pool);
+  s = pstrdup (session_pool, resolve_addr_in_maps (pool, addr));
+  delete_pool (pool);
+
+#if RES_DEBUG
+  fprintf (stderr, "resolved address %lx as %s\n", addr, s);
+#endif
+
+  return s;
+}
+
+static const char *
+resolve_addr_in_maps (pool pool, unsigned long addr)
+{
+  FILE *fp;
+  char *maps_filename;
+#define RES_BUF_SIZE 2048
+  char *buffer;
+  char *filename, *t;
+  unsigned long low, high, offset;
+
+  if (addr == 0) return "NULL";
+
+  maps_filename = psprintf (pool, "/proc/%d/maps", (int) getpid ());
+
+  fp = fopen (maps_filename, "r");
+  if (fp == 0) return resolve_addr_with_dladdr (pool, addr);
+
+  buffer = pmalloc (pool, RES_BUF_SIZE);
+
+  /* Read the maps file until we find the appropriate address range. */
+  while (fgets (buffer, RES_BUF_SIZE, fp) != 0)
+    {
+      if (buffer[strlen(buffer)-1] == '\n')
+       buffer[strlen(buffer)-1] = '\0';
+
+      if (sscanf (buffer, "%lx-%lx r-xp %lx", &low, &high, &offset) != 3)
+       continue;
+
+#if RES_DEBUG
+      fprintf (stderr, "\t%lx-%lx offset %lx\n", low, high, offset);
+#endif
+      if (!(low <= addr && addr < high))
+       continue;
+
+      /* Found the address, so we can close the file. */
+      fclose (fp);
+
+      /* Find the filename (just the basename). */
+      filename = strrchr (buffer, '/');
+      if (!filename) return resolve_addr_with_dladdr (pool, addr);
+      filename++;
+
+      t = strchr (filename, '.');
+      if (t) *t = '\0';
+
+#if RES_DEBUG
+      fprintf (stderr, "\tbase filename = %s\n", filename);
+#endif
+
+      return resolve_addr_in_symtab (pool, addr, filename,
+                                    low, offset);
+    }
+
+  /* Address not found. Go to the back-up approach. */
+  fclose (fp);
+
+  return resolve_addr_with_dladdr (pool, addr);
+#undef RES_BUF_SIZE
+}
+
+static const char *
+resolve_addr_in_symtab (pool pool, unsigned long addr,
+                       const char *filename, unsigned long low,
+                       unsigned long offset)
+{
+  FILE *fp;
+#define RES_BUF_SIZE 2048
+  char *symtabfile, *buffer, *symname, *lastsymname = "(none)";
+  unsigned long lastsymaddr = 0, symaddr, diff;
+
+  /* Look for a matching symtab file. */
+  symtabfile = psprintf (pool, SYMTABSDIR "/%s.syms", filename);
+
+  fp = fopen (symtabfile, "r");
+  if (!fp) return resolve_addr_with_dladdr (pool, addr);
+
+  buffer = pmalloc (pool, RES_BUF_SIZE);
+
+  /* Read the symbols from the symtab file until one matches. */
+  while (fgets (buffer, RES_BUF_SIZE, fp) != 0)
+    {
+      if (buffer[strlen(buffer)-1] == '\n')
+       buffer[strlen(buffer)-1] = '\0';
+
+      symname = strchr (buffer, ' ');
+      if (!symname) continue;
+      *symname = '\0';
+      symname++;
+
+      if (sscanf (buffer, "%lx", &symaddr) != 1) continue;
+
+#if RES_DEBUG
+      fprintf (stderr, "\t%s: %lx %s\n", symtabfile, symaddr, symname);
+#endif
+
+      /* Work out the in-memory address of this symbol. */
+      symaddr += low - offset;
+
+      /* If we've gone past the address, then the symbol must be the
+       * previous symbol.
+       */
+      if (symaddr > addr)
+       goto found_it;
+
+      lastsymaddr = symaddr;
+      lastsymname = pstrdup (pool, symname);
+    }
+
+ found_it:                     /* It's the previous symbol. */
+  fclose (fp);
+
+  diff = addr - lastsymaddr;
+  if (diff != 0)
+    return psprintf (pool, "%s+0x%lx (%s)", lastsymname, diff, filename);
+  else
+    return psprintf (pool, "%s (%s)", lastsymname, filename);
+
+#undef RES_BUF_SIZE
+}
+
+static const char *
+resolve_addr_with_dladdr (pool pool, unsigned long addr)
+{
+#if defined(HAVE_DLFCN_H) && defined(HAVE_DLADDR)
+  int r;
+  Dl_info info;
+
+  r = dladdr ((void *) addr, &info);
+  if (r != 0 && info.dli_saddr != 0)
+    {
+      unsigned long real_addr = (unsigned long) info.dli_saddr;
+      unsigned long diff = addr - real_addr;
+
+      if (diff != 0)
+       return psprintf (pool, "%s+0x%lx (%s)",
+                        info.dli_sname, diff, info.dli_fname);
+      else
+       return psprintf (pool, "%s (%s)", info.dli_sname, info.dli_fname);
+    }
+  else
+    return psprintf (pool, "%p", (void *) addr);
+#else
+  return psprintf (pool, "%p", (void *) addr);
+#endif
+}
+
+/*----- End of symbol resolution code. -----*/
diff --git a/calendar/.cvsignore b/calendar/.cvsignore
new file mode 100644 (file)
index 0000000..1964f99
--- /dev/null
@@ -0,0 +1 @@
+build-*
\ No newline at end of file
diff --git a/calendar/ml_calendar.c b/calendar/ml_calendar.c
new file mode 100644 (file)
index 0000000..1f5dccd
--- /dev/null
@@ -0,0 +1,434 @@
+/* Monolith calendar widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_calendar.c,v 1.7 2003/02/22 15:34:25 rich Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+
+#include <pthr_dbi.h>
+
+#include <monolith.h>
+#include <ml_widget.h>
+#include <ml_button.h>
+#include <ml_table_layout.h>
+#include <ml_vertical_layout.h>
+#include <ml_horizontal_layout.h>
+#include <ml_text_label.h>
+#include <ml_form.h>
+#include <ml_form_input.h>
+#include <ml_form_textarea.h>
+#include <ml_form_select.h>
+#include <ml_form_text.h>
+#include <ml_form_submit.h>
+
+#include "ml_calendar_lib.h"
+#include "ml_calendar_month.h"
+#include "ml_calendar_day.h"
+#include "ml_calendar_notes.h"
+#include "ml_calendar_visible.h"
+#include "ml_calendar.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations calendar_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_calendar
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_session session;          /* Current session. */
+  const char *conninfo;                /* Database connection. */
+  vector calendars;            /* Visible calendars (resource IDs). */
+  int yyyy, mm, dd;            /* Currently visible year, month, day. */
+
+  ml_table_layout tbl;         /* Top-level table layout. */
+  ml_button prev_month;                /* Previous month button. */
+  ml_text_label month_lbl;     /* Month label. */
+  ml_button next_month;                /* Next month button. */
+  ml_button prev_year;         /* Previous year button. */
+  ml_text_label year_lbl;      /* Year label. */
+  ml_button next_year;         /* Next year button. */
+  ml_calendar_month month;     /* Month view (left hand side). */
+  ml_button prev_day;          /* Previous day button. */
+  ml_button today;             /* Today button. */
+  ml_button next_day;          /* Next day button. */
+  ml_calendar_notes notes;     /* Day notes. */
+  ml_calendar_day day;         /* Day view (right hand side). */
+  ml_calendar_visible visible; /* Visible calendar controls. */
+};
+
+static void prev_month (ml_session, void *);
+static void next_month (ml_session, void *);
+static void prev_year (ml_session, void *);
+static void next_year (ml_session, void *);
+static void prev_day (ml_session, void *);
+static void today (ml_session, void *);
+static void next_day (ml_session, void *);
+
+ml_calendar
+new_ml_calendar (pool pool, ml_session session, const char *conninfo,
+                const char *res_name)
+{
+  ml_calendar w = pmalloc (pool, sizeof *w);
+  ml_vertical_layout vert;
+  ml_horizontal_layout horiz;
+
+  w->ops = &calendar_ops;
+  w->pool = pool;
+  w->session = session;
+  w->conninfo = conninfo;
+  w->calendars = new_vector (pool, int);
+
+  w->visible = 0;
+  if (ml_calendar_add_calendar (w, res_name) == -1)
+    return 0;
+
+  /* We won't initialise these until the end of this function, so set
+   * these to known bad values in case one of the constructors below
+   * accidentally tries to use them.
+   */
+  w->yyyy = w->mm = w->dd = -1;
+
+  /* Create the calendar layout. */
+  w->tbl = new_ml_table_layout (pool, 2, 2);
+
+  vert = new_ml_vertical_layout (pool);
+
+  horiz = new_ml_horizontal_layout (pool);
+  w->prev_month = new_ml_button (pool, "&lt;&lt;");
+  ml_button_set_callback (w->prev_month, prev_month, session, w);
+  ml_widget_set_property (w->prev_month, "button.style", "link");
+  ml_horizontal_layout_pack (horiz, w->prev_month);
+  w->month_lbl = new_ml_text_label (pool, 0);
+  ml_horizontal_layout_pack (horiz, w->month_lbl);
+  w->next_month = new_ml_button (pool, "&gt;&gt;");
+  ml_button_set_callback (w->next_month, next_month, session, w);
+  ml_widget_set_property (w->next_month, "button.style", "link");
+  ml_horizontal_layout_pack (horiz, w->next_month);
+  w->prev_year = new_ml_button (pool, "&lt;&lt;");
+  ml_button_set_callback (w->prev_year, prev_year, session, w);
+  ml_widget_set_property (w->prev_year, "button.style", "link");
+  ml_horizontal_layout_pack (horiz, w->prev_year);
+  w->year_lbl = new_ml_text_label (pool, 0);
+  ml_horizontal_layout_pack (horiz, w->year_lbl);
+  w->next_year = new_ml_button (pool, "&gt;&gt;");
+  ml_button_set_callback (w->next_year, next_year, session, w);
+  ml_widget_set_property (w->next_year, "button.style", "link");
+  ml_horizontal_layout_pack (horiz, w->next_year);
+  ml_vertical_layout_pack (vert, horiz);
+
+  w->month = new_ml_calendar_month (pool, session, conninfo, w);
+  ml_vertical_layout_pack (vert, w->month);
+
+  horiz = new_ml_horizontal_layout (pool);
+  w->prev_day = new_ml_button (pool, "Prev");
+  ml_button_set_callback (w->prev_day, prev_day, session, w);
+  ml_horizontal_layout_pack (horiz, w->prev_day);
+  w->today = new_ml_button (pool, "Today");
+  ml_button_set_callback (w->today, today, session, w);
+  ml_horizontal_layout_pack (horiz, w->today);
+  w->next_day = new_ml_button (pool, "Next");
+  ml_button_set_callback (w->next_day, next_day, session, w);
+  ml_horizontal_layout_pack (horiz, w->next_day);
+  ml_vertical_layout_pack (vert, horiz);
+
+  w->notes = new_ml_calendar_notes (pool, session, conninfo, w);
+  ml_vertical_layout_pack (vert, w->notes);
+
+  ml_table_layout_pack (w->tbl, vert, 0, 0);
+  ml_table_layout_set_align (w->tbl, 0, 0, "center");
+  ml_table_layout_set_valign (w->tbl, 0, 0, "top");
+
+  w->day = new_ml_calendar_day (pool, session, conninfo, w);
+  ml_table_layout_pack (w->tbl, w->day, 0, 1);
+  ml_table_layout_set_valign (w->tbl, 0, 1, "top");
+
+  w->visible = new_ml_calendar_visible (pool, session, conninfo, w);
+  ml_table_layout_pack (w->tbl, w->visible, 1, 0);
+  ml_table_layout_set_colspan (w->tbl, 1, 0, 2);
+  ml_table_layout_set_valign (w->tbl, 1, 0, "top");
+
+  /* Update visible calendars. */
+  ml_calendar_visible_update (w->visible);
+
+  /* Set the calendar to today's date. */
+  today (session, w);
+
+  return w;
+}
+
+int
+ml_calendar_add_calendar (ml_calendar w, const char *res_name)
+{
+  db_handle dbh;
+  st_handle sth;
+  int resid;
+
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  /* Get the resource ID. */
+  sth = st_prepare_cached
+    (dbh,
+     "select r.resid from ml_resources r, ml_calendar c "
+     "where r.name = ? and r.resid = c.resid",
+     DBI_STRING);
+  st_execute (sth, res_name);
+
+  st_bind (sth, 0, resid, DBI_INT);
+
+  if (!st_fetch (sth)) return -1; /* Not found. */
+
+  vector_push_back (w->calendars, resid);
+
+  put_db_handle (dbh);
+
+  if (w->visible)
+    ml_calendar_visible_update (w->visible);
+
+  return 0;
+}
+
+int
+ml_calendar_del_calendar (ml_calendar w, const char *res_name)
+{
+  db_handle dbh;
+  st_handle sth;
+  int resid, i, r;
+
+  /* Don't allow the last calendar to be deleted, no matter what. */
+  if (vector_size (w->calendars) == 1)
+    return -1;
+
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  /* Get the resource ID. */
+  sth = st_prepare_cached
+    (dbh,
+     "select r.resid from ml_resources r, ml_calendar c "
+     "where r.name = ? and r.resid = c.resid",
+     DBI_INT);
+  st_execute (sth, res_name);
+
+  st_bind (sth, 0, resid, DBI_INT);
+
+  if (!st_fetch (sth)) return -1; /* Not found. */
+
+  /* Search for the resource in the list of calendars, and delete it. */
+  for (i = 0; i < vector_size (w->calendars); ++i)
+    {
+      vector_get (w->calendars, i, r);
+      if (r == resid)
+       {
+         vector_erase (w->calendars, i);
+         goto found;
+       }
+    }
+
+  return -1;                   /* Not found. */
+
+ found:
+  put_db_handle (dbh);
+
+  if (w->visible)
+    ml_calendar_visible_update (w->visible);
+
+  return 0;
+}
+
+const vector
+_ml_calendar_get_calendars (ml_calendar w)
+{
+  return w->calendars;
+}
+
+static void
+prev_month (ml_session session, void *vw)
+{
+  ml_calendar w = (ml_calendar) vw;
+  int dd = w->dd, mm = w->mm, yyyy = w->yyyy;
+
+  if (mm == 1 && yyyy == 1900) return;
+
+  mm--;
+  if (mm == 0)
+    {
+      yyyy--;
+      mm = 12;
+    }
+  if (dd > _ml_calendar_days_in_month (mm, yyyy))
+    dd = _ml_calendar_days_in_month (mm, yyyy);
+
+  ml_calendar_set_date (w, yyyy, mm, dd);
+}
+
+static void
+next_month (ml_session session, void *vw)
+{
+  ml_calendar w = (ml_calendar) vw;
+  int dd = w->dd, mm = w->mm, yyyy = w->yyyy;
+
+  mm++;
+  if (mm == 13)
+    {
+      yyyy++;
+      mm = 1;
+    }
+  if (dd > _ml_calendar_days_in_month (mm, yyyy))
+    dd = _ml_calendar_days_in_month (mm, yyyy);
+
+  ml_calendar_set_date (w, yyyy, mm, dd);
+}
+
+static void
+prev_year (ml_session session, void *vw)
+{
+  ml_calendar w = (ml_calendar) vw;
+  int dd = w->dd, mm = w->mm, yyyy = w->yyyy;
+
+  if (yyyy == 1900) return;
+
+  yyyy--;
+  if (dd > _ml_calendar_days_in_month (mm, yyyy))
+    dd = _ml_calendar_days_in_month (mm, yyyy);
+
+  ml_calendar_set_date (w, yyyy, mm, dd);
+}
+
+static void
+next_year (ml_session session, void *vw)
+{
+  ml_calendar w = (ml_calendar) vw;
+  int dd = w->dd, mm = w->mm, yyyy = w->yyyy;
+
+  yyyy++;
+  if (dd > _ml_calendar_days_in_month (mm, yyyy))
+    dd = _ml_calendar_days_in_month (mm, yyyy);
+
+  ml_calendar_set_date (w, yyyy, mm, dd);
+}
+
+static void
+prev_day (ml_session session, void *vw)
+{
+  ml_calendar w = (ml_calendar) vw;
+  int dd = w->dd, mm = w->mm, yyyy = w->yyyy;
+
+  if (dd == 1 && mm == 1 && yyyy == 1900) return;
+
+  dd--;
+  if (dd == 0)
+    {
+      mm--;
+      if (mm == 0)
+       {
+         yyyy--;
+         mm = 12;
+       }
+      dd = _ml_calendar_days_in_month (mm, yyyy);
+    }
+
+  ml_calendar_set_date (w, yyyy, mm, dd);
+}
+
+static void
+today (ml_session session, void *vw)
+{
+  ml_calendar w = (ml_calendar) vw;
+  struct tm *tm;
+  time_t t;
+
+  time (&t);
+  tm = localtime (&t);
+  ml_calendar_set_date (w, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
+}
+
+static void
+next_day (ml_session session, void *vw)
+{
+  ml_calendar w = (ml_calendar) vw;
+  int dd = w->dd, mm = w->mm, yyyy = w->yyyy;
+
+  dd++;
+  if (dd > _ml_calendar_days_in_month (mm, yyyy))
+    {
+      mm++;
+      if (mm == 13)
+       {
+         yyyy++;
+         mm = 1;
+       }
+      dd = 1;
+    }
+
+  ml_calendar_set_date (w, yyyy, mm, dd);
+}
+
+extern void
+ml_calendar_set_date (ml_calendar w, int yyyy, int mm, int dd)
+{
+  /* Verify that this is a correct date. */
+  assert (yyyy >= 1900);
+  assert (1 <= mm && mm <= 12);
+  assert (1 <= dd && dd <= _ml_calendar_days_in_month (mm, yyyy));
+
+  /* Set the date and update the widgets. */
+  w->yyyy = yyyy;
+  w->mm = mm;
+  w->dd = dd;
+
+  ml_widget_set_property (w->month_lbl, "text",
+                         _ml_calendar_text_month (w->mm));
+  ml_widget_set_property (w->year_lbl, "text", pitoa (w->pool, w->yyyy));
+
+  ml_calendar_month_set_date (w->month, yyyy, mm, dd);
+  ml_calendar_day_set_date (w->day, yyyy, mm, dd);
+  ml_calendar_notes_set_date (w->notes, yyyy, mm, dd);
+}
+
+extern void
+ml_calendar_get_date (ml_calendar w, int *yyyy, int *mm, int *dd)
+{
+  *yyyy = w->yyyy;
+  *mm = w->mm;
+  *dd = w->dd;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_calendar w = (ml_calendar) vw;
+
+  /* Repaint the top-level table layout widget. */
+  ml_widget_repaint (w->tbl, session, windowid, io);
+}
diff --git a/calendar/ml_calendar.h b/calendar/ml_calendar.h
new file mode 100644 (file)
index 0000000..8bba1d1
--- /dev/null
@@ -0,0 +1,80 @@
+/* Monolith calendar widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_calendar.h,v 1.4 2003/02/22 15:34:25 rich Exp $
+ */
+
+#ifndef ML_CALENDAR_H
+#define ML_CALENDAR_H
+
+#include <vector.h>
+
+#include <monolith.h>
+
+struct ml_calendar;
+typedef struct ml_calendar *ml_calendar;
+
+/* Function: new_ml_calendar - monolith calendar widget
+ * Function: ml_calendar_add_calendar
+ * Function: ml_calendar_del_calendar
+ * Function: ml_calendar_set_date
+ * Function: ml_calendar_get_date
+ *
+ * This is the monolith calendar widget. It supports single and
+ * recurring events, alerts, shared calendars, and views of multiple
+ * calendars.
+ *
+ * The schema for this widget can be found in
+ * @code{sql/ml_calendar_create.sql}.
+ *
+ * @code{new_ml_calendar} creates a new calendar widget. @code{pool}
+ * is the pool for allocations, @code{session} is the current session, and
+ * @code{dhf} is a database handle factory. @code{res_name} is the name
+ * of the calendar resource (from the @code{ml_resources} table).
+ *
+ * The function returns the calendar widget, or @code{NULL} if there
+ * was a problem, such as the calendar not existing.
+ *
+ * The calendar widget can view multiple calendars (there must be
+ * at least one calendar in view at all times).
+ * @code{ml_calendar_add_calendar} adds another calendar to the
+ * list of calendars visible. @code{ml_calendar_del_calendar}
+ * removes a calendar. These functions return 0 on success or
+ * -1 on failure (including attempting to delete all the visible
+ * calendars).
+ *
+ * @code{ml_calendar_(set|get)_date} updates the currently displayed
+ * date in the calendar. The @code{ml_calendar_set_date} function changes
+ * the displayed date to the given @code{yyyy/mm/dd}. This date
+ * must be correct, else the calendar throws an assertion error.
+ * Note in particular that the calendar does not support dates
+ * prior to 1900/1/1 and will throw an error if given such a date.
+ * Note also that @code{yyyy} must be a full four-digit year.
+ * @code{ml_calendar_get_date} returns the current date in the
+ * variables pointed to by @code{yyyy}, @code{mm} and @code{dd} (which
+ * are all pointers to @code{int}).
+ */
+extern ml_calendar new_ml_calendar (pool, ml_session, const char *conninfo, const char *res_name);
+extern int ml_calendar_add_calendar (ml_calendar, const char *res_name);
+extern int ml_calendar_del_calendar (ml_calendar, const char *res_name);
+extern void ml_calendar_set_date (ml_calendar, int yyyy, int mm, int dd);
+extern void ml_calendar_get_date (ml_calendar, int *yyyy, int *mm, int *dd);
+
+/* This function returns a list of all calendar resources. */
+extern const vector _ml_calendar_get_calendars (ml_calendar);
+
+#endif /* ML_CALENDAR_H */
diff --git a/calendar/ml_calendar_day.c b/calendar/ml_calendar_day.c
new file mode 100644 (file)
index 0000000..517aa5d
--- /dev/null
@@ -0,0 +1,828 @@
+/* Monolith calendar widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_calendar_day.c,v 1.7 2003/02/22 15:34:25 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+
+#include <pthr_dbi.h>
+
+#include <monolith.h>
+#include <ml_widget.h>
+#include <ml_window.h>
+#include <ml_dialog.h>
+#include <ml_iframe.h>
+#include <ml_button.h>
+#include <ml_table_layout.h>
+#include <ml_flow_layout.h>
+#include <ml_form_layout.h>
+#include <ml_text_label.h>
+#include <ml_form.h>
+#include <ml_form_input.h>
+#include <ml_form_text.h>
+#include <ml_form_textarea.h>
+#include <ml_form_select.h>
+#include <ml_form_submit.h>
+
+#include "ml_calendar_lib.h"
+#include "ml_calendar_day.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations calendar_day_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_calendar_day
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_session session;          /* Current session. */
+  const char *conninfo;                /* Database connection. */
+  ml_calendar calendar;                /* Parent calendar. */
+  int yyyy, mm, dd;            /* Current date. */
+
+  ml_iframe iframe;            /* The iframe widget. */
+  ml_table_layout tbl;         /* Top-level layout. */
+  ml_button b[24][2];          /* One button for each half hour of the day. */
+
+  /* This is the new event form: */
+  ml_form_select f_resid;      /* Which calendar. */
+  vector f_resids;             /* List of calendars. */
+  ml_form_select f_start;      /* Start time. */
+  ml_form_select f_length;     /* Length. */
+  ml_form_text f_subject;      /* Subject (title). */
+  ml_form_textarea f_body;     /* Body / note. */
+
+  /* The above fields are also used on the edit event form, plus these: */
+  int eventid;                 /* Event ID we are editing. */
+  ml_form_submit edit, delete; /* Which button was pressed. */
+};
+
+/* This is the parameter to the time_button_press function. */
+struct time_button_data
+{
+  int hh, mm;
+  ml_calendar_day w;
+};
+
+/* This is the parameter to the event_button_press function. */
+struct event_button_data
+{
+  int eventid;
+  ml_calendar_day w;
+};
+
+static pool cal_day_pool;      /* Pool for global allocations. */
+static vector start_times;     /* List of start times (for forms). */
+static vector lengths;         /* List of lengths (for forms). */
+static ml_text_label spacer;   /* Used for spacing things out. */
+
+/* The global start_times vector contains these. */
+struct start_times_entry
+{
+  const char *text;            /* Text label. */
+  int hh, mm;                  /* Actual start time. */
+};
+
+/* The global lengths vector contains these. */
+struct lengths_entry
+{
+  const char *text;            /* Text label. */
+  int hh, mm;                  /* Actual length in hours, mins. */
+};
+
+static void calendar_day_init (void) __attribute__ ((constructor));
+static void calendar_day_stop (void) __attribute__ ((destructor));
+
+static void make_window (ml_session session, void *vw);
+static void update_table (ml_calendar_day w);
+static void time_button_press (ml_session session, void *vdata);
+static void add_event (ml_session session, void *vw);
+static void event_button_press (ml_session session, void *vdata);
+static void edit_event (ml_session session, void *vw);
+
+static void
+calendar_day_init ()
+{
+  struct start_times_entry se;
+  struct lengths_entry le;
+  int hh, mm;
+
+  cal_day_pool = new_subpool (global_pool);
+  start_times = new_vector (cal_day_pool, struct start_times_entry);
+  lengths = new_vector (cal_day_pool, struct lengths_entry);
+
+  /* Populate the start_times array. */
+  for (hh = 0; hh < 24; ++hh)
+    for (mm = 0; mm < 60; mm += 15)
+      {
+       se.text = psprintf (cal_day_pool, "%02d:%02d", hh, mm);
+       se.hh = hh;
+       se.mm = mm;
+       vector_push_back (start_times, se);
+      }
+
+  /* Populate the lengths array. */
+  le.text = "&frac14 hour"; le.hh = 0; le.mm = 15;
+  vector_push_back (lengths, le);
+  le.text = "&frac12 hour"; le.hh = 0; le.mm = 30;
+  vector_push_back (lengths, le);
+  le.text = "1 hour"; le.hh = 1; le.mm = 0;
+  vector_push_back (lengths, le);
+  le.text = "1&frac12 hours"; le.hh = 1; le.mm = 30;
+  vector_push_back (lengths, le);
+  le.text = "2 hours"; le.hh = 2; le.mm = 0; vector_push_back (lengths, le);
+  le.text = "3 hours"; le.hh = 3; le.mm = 0; vector_push_back (lengths, le);
+  le.text = "4 hours"; le.hh = 4; le.mm = 0; vector_push_back (lengths, le);
+  le.text = "5 hours"; le.hh = 5; le.mm = 0; vector_push_back (lengths, le);
+  le.text = "6 hours"; le.hh = 6; le.mm = 0; vector_push_back (lengths, le);
+  le.text = "7 hours"; le.hh = 7; le.mm = 0; vector_push_back (lengths, le);
+  le.text = "8 hours"; le.hh = 8; le.mm = 0; vector_push_back (lengths, le);
+  le.text = "9 hours"; le.hh = 9; le.mm = 0; vector_push_back (lengths, le);
+  le.text = "10 hours"; le.hh = 10; le.mm = 0; vector_push_back (lengths, le);
+  le.text = "11 hours"; le.hh = 11; le.mm = 0; vector_push_back (lengths, le);
+  le.text = "12 hours"; le.hh = 12; le.mm = 0; vector_push_back (lengths, le);
+
+  /* This is just used to space events out. */
+  spacer = new_ml_text_label (cal_day_pool, " ");
+}
+
+static void
+calendar_day_stop ()
+{
+  delete_pool (cal_day_pool);
+}
+
+ml_calendar_day
+new_ml_calendar_day (pool pool, ml_session session, const char *conninfo,
+                    ml_calendar calendar)
+{
+  ml_calendar_day w = pmalloc (pool, sizeof *w);
+  struct time_button_data *data;
+  int hh, mm, i;
+
+  w->ops = &calendar_day_ops;
+  w->pool = pool;
+  w->session = session;
+  w->conninfo = conninfo;
+  w->calendar = calendar;
+
+  /* The parent calendar must call ml_calendar_day_set_date to set these
+   * before using this widget.
+   */
+  w->yyyy = w->mm = w->dd = -1;
+
+  /* The widget is really an iframe. */
+  w->iframe = new_ml_iframe (pool, make_window, session, w, 0);
+  ml_widget_set_property (w->iframe, "height", 500);
+  ml_widget_set_property (w->iframe, "width", 400);
+
+  /* Create the top-level widgets. */
+  w->tbl = new_ml_table_layout (pool, 48, 2);
+
+  /* Create the buttons and populate them in the table widget. */
+  for (hh = 0; hh < 24; ++hh)
+    for (mm = i = 0; mm <= 30; mm += 30, i++)
+      {
+       data = pmalloc (pool, sizeof *data);
+       data->hh = hh;
+       data->mm = mm;
+       data->w = w;
+
+       if (i == 0)
+         w->b[hh][0] = new_ml_button (pool, psprintf (pool, "%02d:00", hh));
+       else
+         w->b[hh][1] = new_ml_button (pool, "---");
+       ml_button_set_callback (w->b[hh][i], time_button_press, session, data);
+       ml_button_set_popup (w->b[hh][i], "ml_calendar_day_add_event");
+       ml_button_set_popup_size (w->b[hh][i], 640, 300);
+       ml_widget_set_property (w->b[hh][i], "button.style", "link");
+       if (hh >= 9 && hh < 18)
+         ml_widget_set_property (w->b[hh][i], "class", "ml_calendar_hour");
+       else
+         ml_widget_set_property (w->b[hh][i], "class",
+                                 "ml_calendar_hour_off_peak");
+       ml_table_layout_pack (w->tbl, w->b[hh][i], hh*2+i, 0);
+      }
+
+  return w;
+}
+
+void
+ml_calendar_day_set_date (ml_calendar_day w, int yyyy, int mm, int dd)
+{
+  w->yyyy = yyyy;
+  w->mm = mm;
+  w->dd = dd;
+
+  update_table (w);
+}
+
+/* Create the internal window.
+ * XXX This could be more efficiently written. It creates a window each
+ * time the widget is redrawn.
+ */
+static void
+make_window (ml_session session, void *vw)
+{
+  ml_calendar_day w = (ml_calendar_day) vw;
+  ml_window win;
+
+  win = new_ml_window (session, w->pool);
+  ml_window_pack (win, w->tbl);
+  ml_window_scroll_to (win, 0, 360);
+}
+
+/* This form is used for adding events to the calendar. */
+static void
+time_button_press (ml_session session, void *vdata)
+{
+  struct time_button_data *data = (struct time_button_data *) vdata;
+  ml_calendar_day w = data->w;
+  db_handle dbh;
+  st_handle sth;
+  ml_window win;
+  ml_form form;
+  ml_form_layout tbl;
+  ml_form_submit submit;
+  int i, resid;
+  const char *resname;
+
+  win = new_ml_window (w->session, w->pool);
+  form = new_ml_form (w->pool);
+  tbl = new_ml_form_layout (w->pool);
+
+  /* Set the callback on the form. */
+  ml_form_set_callback (form, add_event, session, w);
+  ml_widget_set_property (form, "method", "GET");
+
+  /* Pull out the list of calendar names from the database, and
+   * use this to populate the calendar drop-down menu.
+   */
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  w->f_resid = new_ml_form_select (w->pool, form);
+  w->f_resids = new_vector (w->pool, int);
+
+  sth = st_prepare_cached
+    (dbh,
+     "select c.resid, r.name from ml_calendar c, ml_resources r "
+     "where c.resid in (@) and c.resid = r.resid order by 2",
+     DBI_VECTOR_INT);
+  st_execute (sth, _ml_calendar_get_calendars (w->calendar));
+
+  st_bind (sth, 0, resid, DBI_INT);
+  st_bind (sth, 1, resname, DBI_STRING);
+
+  while (st_fetch (sth))
+    {
+      vector_push_back (w->f_resids, resid);
+      ml_form_select_push_back (w->f_resid, resname);
+      /* XXX Colours. */
+    }
+  ml_form_layout_pack (tbl, "Calendar: ", w->f_resid);
+
+  put_db_handle (dbh);
+
+  /* Start time. */
+  w->f_start = new_ml_form_select (w->pool, form);
+
+  for (i = 0; i < vector_size (start_times); ++i)
+    {
+      struct start_times_entry se;
+
+      vector_get (start_times, i, se);
+      ml_form_select_push_back (w->f_start, se.text);
+      if (se.hh == data->hh && se.mm == data->mm)
+       ml_form_select_set_selection (w->f_start,
+                                     ml_form_select_size (w->f_start) - 1);
+    }
+  ml_form_layout_pack (tbl, "Start time: ", w->f_start);
+
+  /* Length. */
+  w->f_length = new_ml_form_select (w->pool, form);
+
+  for (i = 0; i < vector_size (lengths); ++i)
+    {
+      struct lengths_entry le;
+
+      vector_get (lengths, i, le);
+      ml_form_select_push_back (w->f_length, le.text);
+    }
+
+  ml_form_select_set_selection (w->f_length, 1);
+  ml_form_layout_pack (tbl, "Length: ", w->f_length);
+
+  /* Subject/title. */
+  w->f_subject = new_ml_form_text (w->pool, form);
+  ml_widget_set_property (w->f_subject, "form.text.size", 50);
+  ml_form_layout_pack (tbl, "Title: ", w->f_subject);
+
+  /* Body. */
+  w->f_body = new_ml_form_textarea (w->pool, form, 5, 50);
+  ml_form_layout_pack (tbl, "Notes: ", w->f_body);
+
+  /* Submit button. */
+  /* XXX Cancel button. */
+  submit = new_ml_form_submit (w->pool, form, "Save");
+  ml_form_layout_pack (tbl, 0, submit);
+
+  /* Pack everything into the window. */
+  ml_form_pack (form, tbl);
+  ml_window_pack (win, form);
+}
+
+/* When the form is submitted to save a new event, this function is called. */
+static void
+add_event (ml_session session, void *vw)
+{
+  ml_calendar_day w = (ml_calendar_day) vw;
+  int userid;
+  const char *subject, *body;
+  int si, li;
+  int i, resid;
+  struct start_times_entry se;
+  struct lengths_entry le;
+  db_handle dbh;
+  st_handle sth;
+
+  /* Only logged-in users can post. */
+  /* XXX Resource-level security - later. */
+  userid = ml_session_userid (session);
+  if (!userid)
+    {
+      ml_error_window (w->pool, session,
+                      "You must be logged in to add events to the calendar.",
+                      ML_DIALOG_CLOSE_BUTTON);
+      return;
+    }
+
+  /* Verify that the user has at least given a subject line. If not, just
+   * return, which redisplays the form.
+   */
+  subject = ml_form_input_get_value (w->f_subject);
+  if (!subject || strlen (subject) == 0)
+    return;
+
+  /* Verify the remaining fields are in range. */
+  i = ml_form_select_get_selection (w->f_resid);
+  si = ml_form_select_get_selection (w->f_start);
+  li = ml_form_select_get_selection (w->f_length);
+  if (i < 0 || i >= vector_size (w->f_resids) ||
+      si < 0 || si >= vector_size (start_times) ||
+      li < 0 || li >= vector_size (lengths))
+    return;
+
+  /* Pull the other fields from the form. */
+  body = ml_form_input_get_value (w->f_body);
+  vector_get (w->f_resids, i, resid);
+  vector_get (start_times, si, se);
+  vector_get (lengths, li, le);
+
+  /* Save it in the database. */
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  sth = st_prepare_cached
+    (dbh,
+     "insert into ml_calendar_events (resid, start_time, length, subject, "
+     "body, author, original_ip) "
+     "values (?, ?, ?, ?, ?, ?, ?)",
+     DBI_INT, DBI_STRING, DBI_STRING, DBI_STRING, DBI_STRING,
+     DBI_INT, DBI_STRING);
+  st_execute (sth,
+             resid,
+             psprintf (w->pool, "%04d/%02d/%02d %02d:%02d:00",
+                       w->yyyy, w->mm, w->dd, se.hh, se.mm),
+             psprintf (w->pool, "%d hours %d mins", le.hh, le.mm),
+             subject, body,
+             userid,
+             ml_session_get_peernamestr (session));
+
+  /* Commit change to the database. */
+  db_commit (dbh);
+  put_db_handle (dbh);
+
+  /* Print confirmation page. */
+  ml_ok_window (w->pool, session,
+               "That event was added to the calendar.",
+               ML_DIALOG_CLOSE_BUTTON|ML_DIALOG_CLOSE_RELOAD_OPENER);
+  update_table (w);
+}
+
+static void
+event_button_press (ml_session session, void *vdata)
+{
+  struct event_button_data *data = (struct event_button_data *) vdata;
+  ml_calendar_day w = data->w;
+  db_handle dbh;
+  st_handle sth;
+  ml_window win;
+  ml_form form;
+  ml_form_layout tbl;
+  ml_flow_layout buttons;
+  int i, t, resid;
+  const char *resname, *subject, *body;
+  struct dbi_timestamp start_time;
+  struct dbi_interval length;
+
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  /* Pull out the current values of the fields from the database. */
+  sth = st_prepare_cached
+    (dbh,
+     "select resid, start_time, length, subject, body "
+     "  from ml_calendar_events "
+     " where id = ? and resid in (@)", DBI_INT, DBI_VECTOR_INT);
+  st_execute (sth, data->eventid, _ml_calendar_get_calendars (w->calendar));
+
+  st_bind (sth, 0, resid, DBI_INT);
+  st_bind (sth, 1, start_time, DBI_TIMESTAMP);
+  st_bind (sth, 2, length, DBI_INTERVAL);
+  st_bind (sth, 3, subject, DBI_STRING);
+  st_bind (sth, 4, body, DBI_STRING);
+
+  if (!st_fetch (sth))
+    {
+      ml_error_window (w->pool, session,
+                      "I cannot find that calendar event. Perhaps it has "
+                      "been deleted by another user, or it is in a calendar "
+                      "which you can no longer see.",
+                      ML_DIALOG_CLOSE_BUTTON);
+      return;
+    }
+
+  /* Save the event ID in the widget structure. We need it in edit_event. */
+  w->eventid = data->eventid;
+
+  win = new_ml_window (session, w->pool);
+  form = new_ml_form (w->pool);
+  tbl = new_ml_form_layout (w->pool);
+
+  /* Set the callback on the form. */
+  ml_form_set_callback (form, edit_event, session, w);
+  ml_widget_set_property (form, "method", "GET");
+
+  /* Pull out the list of calendar names from the database, and
+   * use this to populate the calendar drop-down menu.
+   */
+  w->f_resid = new_ml_form_select (w->pool, form);
+  w->f_resids = new_vector (w->pool, int);
+
+  sth = st_prepare_cached
+    (dbh,
+     "select c.resid, r.name from ml_calendar c, ml_resources r "
+     "where c.resid in (@) and c.resid = r.resid order by 2",
+     DBI_VECTOR_INT);
+  st_execute (sth, _ml_calendar_get_calendars (w->calendar));
+
+  st_bind (sth, 0, t, DBI_INT);
+  st_bind (sth, 1, resname, DBI_STRING);
+
+  while (st_fetch (sth))
+    {
+      vector_push_back (w->f_resids, resid);
+      ml_form_select_push_back (w->f_resid, resname);
+      /* XXX Colours. */
+      if (t == resid)
+       ml_form_select_set_selection (w->f_resid,
+                                     ml_form_select_size (w->f_resid) - 1);
+    }
+  ml_form_layout_pack (tbl, "Calendar: ", w->f_resid);
+
+  /* Start time. */
+  w->f_start = new_ml_form_select (w->pool, form);
+
+  for (i = 0; i < vector_size (start_times); ++i)
+    {
+      struct start_times_entry se;
+
+      vector_get (start_times, i, se);
+      ml_form_select_push_back (w->f_start, se.text);
+      /* XXX This doesn't work for events spanning across day boundaries. */
+      if (se.hh == start_time.hour && se.mm == start_time.min)
+       ml_form_select_set_selection (w->f_start,
+                                     ml_form_select_size (w->f_start) - 1);
+    }
+  ml_form_layout_pack (tbl, "Start time: ", w->f_start);
+
+  /* Length. */
+  w->f_length = new_ml_form_select (w->pool, form);
+
+  for (i = 0; i < vector_size (lengths); ++i)
+    {
+      struct lengths_entry le;
+
+      vector_get (lengths, i, le);
+      ml_form_select_push_back (w->f_length, le.text);
+      if (le.hh == length.hours && le.mm == length.mins)
+       ml_form_select_set_selection (w->f_length,
+                                     ml_form_select_size (w->f_length) - 1);
+    }
+
+  ml_form_layout_pack (tbl, "Length: ", w->f_length);
+
+  /* Subject/title. */
+  w->f_subject = new_ml_form_text (w->pool, form);
+  ml_widget_set_property (w->f_subject, "form.text.size", 50);
+  if (subject)
+    ml_form_input_set_value (w->f_subject, subject);
+  ml_form_layout_pack (tbl, "Title: ", w->f_subject);
+
+  /* Body. */
+  w->f_body = new_ml_form_textarea (w->pool, form, 5, 50);
+  if (body)
+    ml_form_input_set_value (w->f_body, body);
+  ml_form_layout_pack (tbl, "Notes: ", w->f_body);
+
+  /* Submit buttons. */
+  buttons = new_ml_flow_layout (w->pool);
+  w->edit = new_ml_form_submit (w->pool, form, "Edit");
+  ml_flow_layout_pack (buttons, w->edit);
+  w->delete = new_ml_form_submit (w->pool, form, "Delete");
+  ml_flow_layout_pack (buttons, w->delete);
+
+  ml_form_layout_pack (tbl, 0, buttons);
+
+  /* Pack everything into the window. */
+  ml_form_pack (form, tbl);
+  ml_window_pack (win, form);
+}
+
+/* When the edit or delete button is pressed, this function is called. */
+static void
+edit_event (ml_session session, void *vw)
+{
+  ml_calendar_day w = (ml_calendar_day) vw;
+  int userid;
+  int edit_pressed, delete_pressed;
+  const char *s, *subject, *body;
+  int si, li;
+  int i, resid;
+  struct start_times_entry se;
+  struct lengths_entry le;
+  db_handle dbh;
+  st_handle sth;
+
+  /* Only logged-in users can post. */
+  /* XXX Resource-level security - later. */
+  userid = ml_session_userid (session);
+  if (!userid)
+    {
+      ml_error_window (w->pool, session,
+                      "You must be logged in to change "
+                      "events in the calendar.",
+                      ML_DIALOG_CLOSE_BUTTON);
+      return;
+    }
+
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  /* Which submit button was pressed? */
+  s = ml_form_input_get_value (w->edit);
+  edit_pressed = s && strlen (s) > 0;
+
+  s = ml_form_input_get_value (w->delete);
+  delete_pressed = s && strlen (s) > 0;
+
+  assert ((edit_pressed || delete_pressed) &&
+         !(edit_pressed && delete_pressed));
+
+  /* Delete button pressed? */
+  if (delete_pressed)
+    {
+      sth = st_prepare_cached
+       (dbh,
+        "delete from ml_calendar_events "
+        "where id = ? and resid in (@)",
+        DBI_INT, DBI_VECTOR_INT);
+      st_execute (sth, w->eventid, _ml_calendar_get_calendars (w->calendar));
+
+      /* Commit change to the database. */
+      db_commit (dbh);
+      put_db_handle (dbh);
+
+      /* Print confirmation page. */
+      ml_ok_window (w->pool, session,
+                   "That event was deleted.",
+                   ML_DIALOG_CLOSE_BUTTON|ML_DIALOG_CLOSE_RELOAD_OPENER);
+      update_table (w);
+      return;
+    }
+
+  /* Verify that the user has at least given a subject line. If not, just
+   * return, which redisplays the form.
+   */
+  subject = ml_form_input_get_value (w->f_subject);
+  if (!subject || strlen (subject) == 0)
+    return;
+
+  /* Verify the remaining fields are in range. */
+  i = ml_form_select_get_selection (w->f_resid);
+  si = ml_form_select_get_selection (w->f_start);
+  li = ml_form_select_get_selection (w->f_length);
+  if (i < 0 || i >= vector_size (w->f_resids) ||
+      si < 0 || si >= vector_size (start_times) ||
+      li < 0 || li >= vector_size (lengths))
+    return;
+
+  /* Pull the other fields from the form. */
+  body = ml_form_input_get_value (w->f_body);
+  vector_get (w->f_resids, i, resid);
+  vector_get (start_times, si, se);
+  vector_get (lengths, li, le);
+
+  /* Update the database. */
+  sth = st_prepare_cached
+    (dbh,
+     "update ml_calendar_events "
+     "set resid = ?, start_time = ?, length = ?, subject = ?, body = ?, "
+     "last_modified_date = current_timestamp "
+     "where id = ? and resid in (@)",
+     DBI_INT, DBI_STRING, DBI_STRING, DBI_STRING, DBI_STRING,
+     DBI_INT, DBI_VECTOR_INT);
+  st_execute (sth,
+             resid,
+             psprintf (w->pool, "%04d/%02d/%02d %02d:%02d:00",
+                       w->yyyy, w->mm, w->dd, se.hh, se.mm),
+             psprintf (w->pool, "%d hours %d mins", le.hh, le.mm),
+             subject, body,
+             w->eventid, _ml_calendar_get_calendars (w->calendar));
+
+  /* Commit change to the database. */
+  db_commit (dbh);
+  put_db_handle (dbh);
+
+  /* Print confirmation page. */
+  ml_ok_window (w->pool, session,
+               "That event was edited.",
+               ML_DIALOG_CLOSE_BUTTON|ML_DIALOG_CLOSE_RELOAD_OPENER);
+  update_table (w);
+}
+
+/* Update the table from the database. */
+static void
+update_table (ml_calendar_day w)
+{
+  db_handle dbh;
+  st_handle sth;
+  const vector resids = _ml_calendar_get_calendars (w->calendar);
+  int eventid, resid, start_time_hh, start_time_mm, end_time_hh, end_time_mm;
+  const char *subject, *body, *start_of_day;
+  ml_button b;
+  ml_flow_layout flow[48] = { 0 };
+  int start, end, i;
+  struct event_button_data *data;
+  const char *title;
+
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  /* Read the events from the database. */
+  /* SQL/PostgreSQL could do with having some way to name subexpressions
+   * in a query ...
+   */
+  start_of_day = psprintf (w->pool,
+                          "%04d/%02d/%02d 00:00:00",
+                          w->yyyy, w->mm, w->dd);
+
+  sth = st_prepare_cached
+    (dbh,
+     "select id, resid, subject, body, "
+     "       date_part ('hour', start_time - date ?), "
+     "       date_part ('min', start_time - date ?), "
+     "       date_part ('hour', start_time + length - date ?), "
+     "       date_part ('min', start_time + length - date ?) "
+     "  from ml_calendar_events "
+     " where resid in (@) "
+     "   and length <> '1 day' "
+     "   and start_time + length >= date ? "
+     "   and start_time < date ? + interval '1 day'"
+     " order by 1",
+     DBI_STRING, DBI_STRING, DBI_STRING, DBI_STRING,
+     DBI_VECTOR_INT,
+     DBI_STRING, DBI_STRING);
+
+  st_execute (sth, start_of_day, start_of_day, start_of_day, start_of_day,
+             resids, start_of_day, start_of_day);
+
+  st_bind (sth, 0, eventid, DBI_INT);
+  st_bind (sth, 1, resid, DBI_INT);
+  st_bind (sth, 2, subject, DBI_STRING);
+  st_bind (sth, 3, body, DBI_STRING);
+  st_bind (sth, 4, start_time_hh, DBI_INT);
+  st_bind (sth, 5, start_time_mm, DBI_INT);
+  st_bind (sth, 6, end_time_hh, DBI_INT);
+  st_bind (sth, 7, end_time_mm, DBI_INT);
+
+  while (st_fetch (sth))
+    {
+      data = pmalloc (w->pool, sizeof *data);
+      data->eventid = eventid;
+      data->w = w;
+
+      /* Create the tooltip (title) for this button. */
+      title = psprintf (w->pool, "%02d:%02d - %02d:%02d: %s",
+                       start_time_hh, start_time_mm,
+                       end_time_hh, end_time_mm,
+                       body ? body : "");
+
+      /* Create the button. */
+      b = new_ml_button (w->pool, subject);
+      ml_widget_set_property (b, "button.style", "link");
+      ml_widget_set_property (b, "color", _ml_calendar_colour (resid));
+      ml_widget_set_property (b, "title", title);
+      ml_button_set_callback (b, event_button_press, w->session, data);
+      ml_button_set_popup (b, "ml_calendar_day_edit_event");
+      ml_button_set_popup_size (b, 640, 300);
+
+      /* Work out the start / end indexes. */
+      if (start_time_hh < 0)
+       start = 0;
+      else
+       {
+         assert (start_time_hh <= 24);
+         assert (0 <= start_time_mm && start_time_mm < 60);
+
+         start = start_time_hh * 2 + start_time_mm / 30;
+       }
+
+      if (end_time_hh >= 24)
+       end = 48;
+      else
+       {
+         assert (end_time_hh >= 0);
+         assert (0 <= end_time_mm && end_time_mm < 60);
+
+         end = end_time_hh * 2 + end_time_mm / 30;
+       }
+
+      /* Make sure that short events are displayed. */
+      if (start == end)
+       {
+         assert (end < 48);
+         end++;
+       }
+
+#if 0
+      fprintf (stderr,
+              "fetched event #%d for day %04d/%02d/%02d: starts at %02d:%02d "
+              "ends at %02d:%02d startbox %d endbox %d\n",
+              eventid, w->yyyy, w->mm, w->dd, start_time_hh, start_time_mm,
+              end_time_hh, end_time_mm, start, end);
+#endif
+
+      /* Pack it into the appropriate flow widget. */
+      for (i = start; i < end; ++i)
+       {
+         if (flow[i] == 0)
+           flow[i] = new_ml_flow_layout (w->pool);
+         ml_flow_layout_push_back (flow[i], b);
+         ml_flow_layout_push_back (flow[i], spacer);
+       }
+    }
+
+  /* Copy the flow widgets into the table's second column. */
+  for (i = 0; i < 48; ++i)
+    ml_table_layout_pack (w->tbl, flow[i], i, 1);
+
+  put_db_handle (dbh);
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_calendar_day w = (ml_calendar_day) vw;
+
+  ml_widget_repaint (w->iframe, session, windowid, io);
+}
diff --git a/calendar/ml_calendar_day.h b/calendar/ml_calendar_day.h
new file mode 100644 (file)
index 0000000..91ee284
--- /dev/null
@@ -0,0 +1,34 @@
+/* Monolith calendar widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_calendar_day.h,v 1.3 2003/02/22 15:34:26 rich Exp $
+ */
+
+#ifndef ML_CALENDAR_DAY_H
+#define ML_CALENDAR_DAY_H
+
+#include <monolith.h>
+
+#include "ml_calendar.h"
+
+struct ml_calendar_day;
+typedef struct ml_calendar_day *ml_calendar_day;
+
+extern ml_calendar_day new_ml_calendar_day (pool, ml_session, const char *conninfo, ml_calendar w);
+extern void ml_calendar_day_set_date (ml_calendar_day, int yyyy, int mm, int dd);
+
+#endif /* ML_CALENDAR_DAY_H */
diff --git a/calendar/ml_calendar_lib.c b/calendar/ml_calendar_lib.c
new file mode 100644 (file)
index 0000000..a73a9f5
--- /dev/null
@@ -0,0 +1,94 @@
+/* Monolith calendar widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_calendar_lib.c,v 1.5 2003/02/22 12:56:24 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include "ml_calendar_lib.h"
+
+/* Return the current month as a string. */
+const char *
+_ml_calendar_text_month (int mm)
+{
+  switch (mm) {
+  case 1: return "January";
+  case 2: return "February";
+  case 3: return "March";
+  case 4: return "April";
+  case 5: return "May";
+  case 6: return "June";
+  case 7: return "July";
+  case 8: return "August";
+  case 9: return "September";
+  case 10: return "October";
+  case 11: return "November";
+  case 12: return "December";
+  }
+  abort ();
+}
+
+/* Return the first weekday of the month (range 0 - 6). I don't know an
+ * easier way to do this.
+ */
+int
+_ml_calendar_first_wday_of_month (int mm, int yyyy)
+{
+  struct tm tm;
+
+  tm.tm_sec = 0;
+  tm.tm_min = 0;
+  tm.tm_hour = 12;
+  tm.tm_mday = 1;
+  tm.tm_mon = mm-1;
+  tm.tm_year = yyyy-1900;
+  tm.tm_wday = -1;
+  tm.tm_yday = -1;
+  tm.tm_isdst = 0;
+  mktime (&tm);
+  assert (tm.tm_wday >= 0);
+  return tm.tm_wday;
+}
+
+/* Map the calendar number to a colour using a fixed table. This isn't
+ * very satisfactory. In future we should allow users to choose the
+ * colours of calendars.
+ */
+const char *
+_ml_calendar_colour (int resid)
+{
+  static const char *cols[8] = {
+    "black",
+    "blue",
+    "red",
+    "magenta",
+    "orange",
+    "green",
+    "pink",
+    "purple"
+  };
+
+  return cols[resid & 7];
+}
diff --git a/calendar/ml_calendar_lib.h b/calendar/ml_calendar_lib.h
new file mode 100644 (file)
index 0000000..da2443d
--- /dev/null
@@ -0,0 +1,39 @@
+/* Monolith calendar widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_calendar_lib.h,v 1.4 2003/02/22 12:56:24 rich Exp $
+ */
+
+#ifndef ML_CALENDAR_LIB_H
+#define ML_CALENDAR_LIB_H
+
+extern const char *_ml_calendar_text_month (int mm);
+extern inline int
+_ml_calendar_days_in_month (int mm, int yyyy)
+{
+  int is_leap_year;
+  static int dm[] = { 0, 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+  if (mm != 2) return dm[mm];
+  is_leap_year = ((yyyy % 4 == 0) && (yyyy % 100 != 0))
+    || (yyyy % 400 == 0);
+  return is_leap_year ? 29 : 28;
+}
+extern int _ml_calendar_first_wday_of_month (int mm, int yyyy);
+extern const char *_ml_calendar_colour (int resid);
+
+#endif /* ML_CALENDAR_LIB_H */
diff --git a/calendar/ml_calendar_month.c b/calendar/ml_calendar_month.c
new file mode 100644 (file)
index 0000000..0b0b5e1
--- /dev/null
@@ -0,0 +1,262 @@
+/* Monolith calendar widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_calendar_month.c,v 1.9 2003/02/22 15:34:26 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <vector.h>
+#include <pstring.h>
+
+#include <pthr_dbi.h>
+
+#include <monolith.h>
+#include <ml_widget.h>
+#include <ml_button.h>
+#include <ml_text_label.h>
+#include <ml_table_layout.h>
+
+#include "ml_calendar_lib.h"
+#include "ml_calendar_month.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations calendar_month_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_calendar_month
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_session session;          /* Current session. */
+  const char *conninfo;                /* Database connection. */
+  ml_calendar calendar;                /* Parent calendar. */
+  int yyyy, mm, dd;            /* Current date. */
+
+  ml_table_layout tbl;         /* Top-level table layout. */
+  ml_text_label days[7];       /* Days of the week labels. */
+  ml_button b[32];             /* Buttons (we reuse these). */
+};
+
+/* This structure is passed to the button callbacks. */
+struct button_data
+{
+  int dd;                      /* Day (1 - 31). */
+  ml_calendar_month w;         /* Widget pointer. */
+};
+
+static void button_press (ml_session, void *);
+static void update_buttons (ml_calendar_month w);
+
+ml_calendar_month
+new_ml_calendar_month (pool pool, ml_session session, const char *conninfo,
+                      ml_calendar calendar)
+{
+  ml_calendar_month w = pmalloc (pool, sizeof *w);
+  int i, j;
+  struct button_data *data;
+
+  w->ops = &calendar_month_ops;
+  w->pool = pool;
+  w->session = session;
+  w->conninfo = conninfo;
+  w->calendar = calendar;
+
+  /* The parent calendar must call ml_calendar_month_set_date to set these
+   * before using this widget.
+   */
+  w->yyyy = w->mm = w->dd = -1;
+
+  /* Create the days of the week labels and buttons. */
+  /* XXX Waiting for ml_text_label and ml_button to give us more control
+   * over the rendering of labels and buttons.
+   */
+  w->days[0] = new_ml_text_label (pool, "Su");
+  ml_widget_set_property (w->days[0], "font.weight", "bold");
+  w->days[1] = new_ml_text_label (pool, "Mo");
+  ml_widget_set_property (w->days[1], "font.weight", "bold");
+  w->days[2] = new_ml_text_label (pool, "Tu");
+  ml_widget_set_property (w->days[2], "font.weight", "bold");
+  w->days[3] = new_ml_text_label (pool, "We");
+  ml_widget_set_property (w->days[3], "font.weight", "bold");
+  w->days[4] = new_ml_text_label (pool, "Th");
+  ml_widget_set_property (w->days[4], "font.weight", "bold");
+  w->days[5] = new_ml_text_label (pool, "Fr");
+  ml_widget_set_property (w->days[5], "font.weight", "bold");
+  w->days[6] = new_ml_text_label (pool, "Sa");
+  ml_widget_set_property (w->days[6], "font.weight", "bold");
+
+  w->b[0] = 0;                 /* This is a dummy entry. */
+
+  for (i = 1; i <= 31; ++i)
+    {
+      data = pmalloc (pool, sizeof *data);
+      w->b[i] = new_ml_button (pool, pitoa (pool, i));
+      data->dd = i;
+      data->w = w;
+      ml_button_set_callback (w->b[i], button_press, session, data);
+    }
+
+  /* Create the top-level table widget, also reused. */
+  w->tbl = new_ml_table_layout (pool, 7, 7);
+  for (i = 0; i < 7; ++i)
+    {
+      ml_table_layout_pack (w->tbl, w->days[i], 0, i);
+      ml_table_layout_set_align (w->tbl, 0, i, "center");
+    }
+  for (j = 1; j < 7; ++j)
+    for (i = 0; i < 7; ++i)
+      ml_table_layout_set_align (w->tbl, j, i, "right");
+
+  return w;
+}
+
+/* Update the date, then update the widget. */
+void
+ml_calendar_month_set_date (ml_calendar_month w, int yyyy, int mm, int dd)
+{
+  int j, i, d, n;
+
+  /* Update the date. */
+  w->yyyy = yyyy;
+  w->mm = mm;
+  w->dd = dd;
+
+  /* Clear the existing table of widgets. */
+  for (j = 1; j < 7; ++j)
+    for (i = 0; i < 7; ++i)
+      ml_table_layout_pack (w->tbl, 0, j, i);
+
+  /* Redraw the month. */
+  j = 1;
+  i = _ml_calendar_first_wday_of_month (mm, yyyy);
+  n = _ml_calendar_days_in_month (mm, yyyy);
+
+  for (d = 1; d <= n; ++d)
+    {
+      ml_table_layout_pack (w->tbl, w->b[d], j, i);
+      i++;
+      if (i == 7)
+       {
+         i = 0;
+         j++;
+       }
+    }
+
+  /* Update buttons from the database. */
+  update_buttons (w);
+}
+
+/* This function is called when a button is pressed. */
+static void
+button_press (ml_session session, void *vdata)
+{
+  struct button_data *data = (struct button_data *) vdata;
+
+  /* This function, in the parent calendar, will eventually call our
+   * ml_calendar_month_set_date.
+   */
+  ml_calendar_set_date (data->w->calendar,
+                       data->w->yyyy, data->w->mm, data->dd);
+}
+
+/* This function updates the state of the buttons from the database,
+ * and also highlights the currently selected day.
+ */
+static void
+update_buttons (ml_calendar_month w)
+{
+  db_handle dbh;
+  st_handle sth;
+  unsigned char flags[32];
+  int dd;
+  const vector resids = _ml_calendar_get_calendars (w->calendar);
+
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  /* Find out which days contain data. Note how with PostgreSQL we
+   * can leave the database to take the strain of date munging. This
+   * is one of the great reasons to use and recommend PostgreSQL.
+   */
+  sth = st_prepare_cached
+    (dbh,
+     "select date_part ('day', start_time) from ml_calendar_events "
+     " where resid in (@) "
+     "   and start_time >= date (? || '/' || ? || '/01') "
+     "   and start_time <  (date (? || '/' || ? || '/01') + "
+     "                      interval '1 month')",
+     DBI_VECTOR_INT, DBI_INT, DBI_INT, DBI_INT, DBI_INT);
+  st_execute (sth, resids, w->yyyy, w->mm, w->yyyy, w->mm);
+
+  st_bind (sth, 0, dd, DBI_INT);
+
+  memset (flags, 0, sizeof flags);
+
+  while (st_fetch (sth))
+    {
+      assert (0 < dd && dd < 32);
+      flags[dd] |= 1;
+    }
+
+  flags[w->dd] |= 2;
+
+  /* Depending on the flags, we now set the class of each button. */
+  for (dd = 1; dd <= 31; ++dd)
+    {
+      switch (flags[dd]) {
+      case 0:
+       ml_widget_set_property (w->b[dd], "class", "ml_calendar_day");
+       break;
+      case 1:
+       ml_widget_set_property (w->b[dd], "class", "ml_calendar_day_events");
+       break;
+      case 2:
+       ml_widget_set_property (w->b[dd], "class", "ml_calendar_day_selected");
+       break;
+      case 3:
+       ml_widget_set_property (w->b[dd], "class",
+                               "ml_calendar_day_events_selected");
+       break;
+      }
+    }
+
+  put_db_handle (dbh);
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_calendar_month w = (ml_calendar_month) vw;
+
+  ml_widget_repaint (w->tbl, session, windowid, io);
+}
diff --git a/calendar/ml_calendar_month.h b/calendar/ml_calendar_month.h
new file mode 100644 (file)
index 0000000..897aa8a
--- /dev/null
@@ -0,0 +1,34 @@
+/* Monolith calendar widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_calendar_month.h,v 1.3 2003/02/22 15:34:26 rich Exp $
+ */
+
+#ifndef ML_CALENDAR_MONTH_H
+#define ML_CALENDAR_MONTH_H
+
+#include <monolith.h>
+
+#include "ml_calendar.h"
+
+struct ml_calendar_month;
+typedef struct ml_calendar_month *ml_calendar_month;
+
+extern ml_calendar_month new_ml_calendar_month (pool, ml_session, const char *conninfo, ml_calendar w);
+extern void ml_calendar_month_set_date (ml_calendar_month, int yyyy, int mm, int dd);
+
+#endif /* ML_CALENDAR_MONTH_H */
diff --git a/calendar/ml_calendar_notes.c b/calendar/ml_calendar_notes.c
new file mode 100644 (file)
index 0000000..b6999a6
--- /dev/null
@@ -0,0 +1,545 @@
+/* Monolith calendar widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_calendar_notes.c,v 1.6 2003/02/22 15:34:26 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+
+#include <pthr_dbi.h>
+
+#include <monolith.h>
+#include <ml_widget.h>
+#include <ml_table_layout.h>
+#include <ml_flow_layout.h>
+#include <ml_vertical_layout.h>
+#include <ml_form_layout.h>
+#include <ml_button.h>
+#include <ml_text_label.h>
+#include <ml_dialog.h>
+#include <ml_form.h>
+#include <ml_form_text.h>
+#include <ml_form_textarea.h>
+#include <ml_form_select.h>
+#include <ml_form_submit.h>
+
+#include "ml_calendar_lib.h"
+#include "ml_calendar_notes.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations calendar_notes_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_calendar_notes
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_session session;          /* Current session. */
+  const char *conninfo;                /* Database connection. */
+  ml_calendar calendar;                /* Parent calendar. */
+  int yyyy, mm, dd;            /* Current date. */
+
+  ml_table_layout top;         /* Top-level layout. */
+  ml_button add;               /* Add a note. */
+  ml_vertical_layout tbl;      /* Table of notes. */
+
+  /* These widgets are used in the add note form. */
+  ml_form_select f_resid;      /* Calendar selected. */
+  vector f_resids;             /* List of calendars. */
+  ml_form_text f_subject;      /* Title. */
+  ml_form_textarea f_body;     /* Notes. */
+
+  /* The above fields are also used on the edit note form, plus these: */
+  int eventid;                 /* Event ID we are editing. */
+  ml_form_submit edit, delete; /* Which button was pressed. */
+};
+
+/* This is the parameter to the note_button_press function. */
+struct note_button_data
+{
+  int eventid;
+  ml_calendar_notes w;
+};
+
+static void update_notes (ml_calendar_notes w);
+static void add_note_button (ml_session session, void *vw);
+static void add_note (ml_session session, void *vw);
+static void note_button_press (ml_session session, void *vdata);
+static void edit_note (ml_session session, void *vw);
+
+ml_calendar_notes
+new_ml_calendar_notes (pool pool, ml_session session, const char *conninfo,
+                      ml_calendar calendar)
+{
+  ml_calendar_notes w = pmalloc (pool, sizeof *w);
+
+  w->ops = &calendar_notes_ops;
+  w->pool = pool;
+  w->session = session;
+  w->conninfo = conninfo;
+  w->calendar = calendar;
+
+  /* The parent calendar must call ml_calendar_month_set_date to set these
+   * before using this widget.
+   */
+  w->yyyy = w->mm = w->dd = -1;
+
+  /* Create the top-level layout and add buttons to it. */
+  w->top = new_ml_table_layout (pool, 2, 1);
+  ml_widget_set_property (w->top, "class", "ml_calendar_notes");
+  w->add = new_ml_button (pool, "Add note");
+  ml_button_set_callback (w->add, add_note_button, session, w);
+  ml_button_set_popup (w->add, "ml_calendar_notes_add_event");
+  ml_button_set_popup_size (w->add, 640, 300);
+  ml_table_layout_pack (w->top, w->add, 1, 0);
+  ml_table_layout_set_align (w->top, 1, 0, "left");
+
+  /* Create the notes area, unpopulated to start with. */
+  w->tbl = new_ml_vertical_layout (pool);
+  ml_widget_set_property (w->tbl, "class", "ml_calendar_notes");
+  ml_table_layout_pack (w->top, w->tbl, 0, 0);
+  ml_table_layout_set_align (w->top, 0, 0, "left");
+
+  return w;
+}
+
+void
+ml_calendar_notes_set_date (ml_calendar_notes w, int yyyy, int mm, int dd)
+{
+  /* Update the date. */
+  w->yyyy = yyyy;
+  w->mm = mm;
+  w->dd = dd;
+
+  update_notes (w);
+}
+
+/* Update the notes area from the database. */
+static void
+update_notes (ml_calendar_notes w)
+{
+  db_handle dbh;
+  st_handle sth;
+  const vector resids = _ml_calendar_get_calendars (w->calendar);
+  int eventid, resid;
+  const char *subject, *body;
+  ml_button b;
+  struct note_button_data *data;
+
+  ml_vertical_layout_clear (w->tbl);
+
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  /* Read the notes from the database. */
+  sth = st_prepare_cached
+    (dbh,
+     "select id, resid, subject, body from ml_calendar_events "
+     " where resid in (@) "
+     "   and length = '1 day' "
+     "   and start_time = date (? || '/' || ? || '/' || ? || ' 00:00:00') "
+     " order by 1",
+     DBI_VECTOR_INT, DBI_INT, DBI_INT, DBI_INT);
+  st_execute (sth, resids, w->yyyy, w->mm, w->dd);
+
+  st_bind (sth, 0, eventid, DBI_INT);
+  st_bind (sth, 1, resid, DBI_INT);
+  st_bind (sth, 2, subject, DBI_STRING);
+  st_bind (sth, 3, body, DBI_STRING);
+
+  while (st_fetch (sth))
+    {
+      data = pmalloc (w->pool, sizeof *data);
+      data->eventid = eventid;
+      data->w = w;
+
+      b = new_ml_button (w->pool, subject);
+      ml_widget_set_property (b, "button.style", "link");
+      ml_widget_set_property (b, "color", _ml_calendar_colour (resid));
+      ml_widget_set_property (b, "title", body);
+      ml_button_set_callback (b, note_button_press, w->session, data);
+      ml_button_set_popup (b, "ml_calendar_notes_edit_event");
+      ml_button_set_popup_size (b, 640, 300);
+
+      ml_vertical_layout_push_back (w->tbl, b);
+    }
+
+  put_db_handle (dbh);
+}
+
+static void
+add_note_button (ml_session session, void *vw)
+{
+  ml_calendar_notes w = (ml_calendar_notes) vw;
+  ml_window win;
+  ml_form form;
+  ml_form_layout tbl;
+  ml_form_submit submit;
+  db_handle dbh;
+  st_handle sth;
+  int resid;
+  const char *resname;
+
+  win = new_ml_window (w->session, w->pool);
+
+  /* Create the form. */
+  form = new_ml_form (w->pool);
+  tbl = new_ml_form_layout (w->pool);
+
+  /* Set the callback on the form. */
+  ml_form_set_callback (form, add_note, session, w);
+  ml_widget_set_property (form, "method", "GET");
+
+  /* Pull out the list of calendar names from the database and populate
+   * the drop-down menu with it.
+   */
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  w->f_resid = new_ml_form_select (w->pool, form);
+  w->f_resids = new_vector (w->pool, int);
+
+  sth = st_prepare_cached
+    (dbh,
+     "select c.resid, r.name from ml_calendar c, ml_resources r "
+     "where c.resid in (@) and c.resid = r.resid order by 2",
+     DBI_VECTOR_INT);
+  st_execute (sth, _ml_calendar_get_calendars (w->calendar));
+
+  st_bind (sth, 0, resid, DBI_INT);
+  st_bind (sth, 1, resname, DBI_STRING);
+
+  while (st_fetch (sth))
+    {
+      vector_push_back (w->f_resids, resid);
+      ml_form_select_push_back (w->f_resid, resname);
+      /* XXX Colours. */
+    }
+  ml_form_layout_pack (tbl, "Calendar: ", w->f_resid);
+
+  put_db_handle (dbh);
+
+  /* Subject/title. */
+  w->f_subject = new_ml_form_text (w->pool, form);
+  ml_widget_set_property (w->f_subject, "form.text.size", 50);
+  ml_form_layout_pack (tbl, "Title: ", w->f_subject);
+
+  /* Body. */
+  w->f_body = new_ml_form_textarea (w->pool, form, 5, 50);
+  ml_form_layout_pack (tbl, "Notes: ", w->f_body);
+
+  /* Submit button. */
+  /* XXX Cancel button. */
+  submit = new_ml_form_submit (w->pool, form, "Save");
+  ml_form_layout_pack (tbl, 0, submit);
+
+  /* Pack everything together. */
+  ml_form_pack (form, tbl);
+  ml_window_pack (win, form);
+}
+
+static void
+add_note (ml_session session, void *vw)
+{
+  ml_calendar_notes w = (ml_calendar_notes) vw;
+  db_handle dbh;
+  st_handle sth;
+  int userid, i, resid;
+  const char *subject, *body;
+
+  /* Only logged-in users can post. */
+  /* XXX Resource-level security - later. */
+  userid = ml_session_userid (session);
+  if (!userid)
+    {
+      ml_error_window (w->pool, session,
+                      "You must be logged in to add events to the calendar.",
+                      ML_DIALOG_CLOSE_BUTTON);
+      return;
+    }
+
+  /* Verify that the user has at least given a subject line. If not, just
+   * return, which redisplays the form.
+   */
+  subject = ml_form_input_get_value (w->f_subject);
+  if (!subject || strlen (subject) == 0)
+    return;
+
+  /* Verify the remaining fields are in range. */
+  i = ml_form_select_get_selection (w->f_resid);
+  if (i < 0 || i >= vector_size (w->f_resids))
+    return;
+
+  /* Pull the other fields from the form. */
+  body = ml_form_input_get_value (w->f_body);
+  vector_get (w->f_resids, i, resid);
+
+  /* Save it in the database. */
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  sth = st_prepare_cached
+    (dbh,
+     "insert into ml_calendar_events (resid, start_time, length, subject, "
+     "body, author, original_ip) "
+     "values (?, ?, '1 day', ?, ?, ?, ?)",
+     DBI_INT, DBI_STRING, DBI_STRING, DBI_STRING, DBI_INT, DBI_STRING);
+  st_execute (sth,
+             resid,
+             psprintf (w->pool, "%04d/%02d/%02d 00:00:00",
+                       w->yyyy, w->mm, w->dd),
+             subject, body,
+             userid,
+             ml_session_get_peernamestr (session));
+
+  /* Commit change to the database. */
+  db_commit (dbh);
+  put_db_handle (dbh);
+
+  /* Print confirmation page. */
+  ml_ok_window (w->pool, session,
+               "That note was added to the calendar.",
+               ML_DIALOG_CLOSE_BUTTON|ML_DIALOG_CLOSE_RELOAD_OPENER);
+  update_notes (w);
+}
+
+static void
+note_button_press (ml_session session, void *vdata)
+{
+  struct note_button_data *data = (struct note_button_data *) vdata;
+  ml_calendar_notes w = data->w;
+  ml_window win;
+  ml_form form;
+  ml_form_layout tbl;
+  ml_flow_layout buttons;
+  db_handle dbh;
+  st_handle sth;
+  int resid, t;
+  const char *resname, *subject, *body;
+
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  /* Pull out the current values of the fields from the database. */
+  sth = st_prepare_cached
+    (dbh,
+     "select resid, subject, body "
+     "  from ml_calendar_events "
+     " where id = ? and resid in (@)", DBI_INT, DBI_VECTOR_INT);
+  st_execute (sth, data->eventid, _ml_calendar_get_calendars (w->calendar));
+
+  st_bind (sth, 0, resid, DBI_INT);
+  st_bind (sth, 1, subject, DBI_STRING);
+  st_bind (sth, 2, body, DBI_STRING);
+
+  if (!st_fetch (sth))
+    {
+      ml_error_window (w->pool, session,
+                      "I cannot find that calendar event. Perhaps it has "
+                      "been deleted by another user, or it is in a calendar "
+                      "which you can no longer see.",
+                      ML_DIALOG_CLOSE_BUTTON);
+      return;
+    }
+
+  /* Save the event ID in the widget structure. We need it in edit_event. */
+  w->eventid = data->eventid;
+
+  win = new_ml_window (w->session, w->pool);
+
+  /* Create the form. */
+  form = new_ml_form (w->pool);
+  tbl = new_ml_form_layout (w->pool);
+
+  /* Set the callback on the form. */
+  ml_form_set_callback (form, edit_note, session, w);
+  ml_widget_set_property (form, "method", "GET");
+
+  /* Pull out the list of calendar names from the database and populate
+   * the drop-down menu with it.
+   */
+  w->f_resid = new_ml_form_select (w->pool, form);
+  w->f_resids = new_vector (w->pool, int);
+
+  sth = st_prepare_cached
+    (dbh,
+     "select c.resid, r.name from ml_calendar c, ml_resources r "
+     "where c.resid in (@) and c.resid = r.resid order by 2",
+     DBI_VECTOR_INT);
+  st_execute (sth, _ml_calendar_get_calendars (w->calendar));
+
+  st_bind (sth, 0, t, DBI_INT);
+  st_bind (sth, 1, resname, DBI_STRING);
+
+  while (st_fetch (sth))
+    {
+      vector_push_back (w->f_resids, resid);
+      ml_form_select_push_back (w->f_resid, resname);
+      /* XXX Colours. */
+      if (t == resid)
+       ml_form_select_set_selection (w->f_resid,
+                                     ml_form_select_size (w->f_resid) - 1);
+    }
+  ml_form_layout_pack (tbl, "Calendar: ", w->f_resid);
+
+  put_db_handle (dbh);
+
+  /* Subject/title. */
+  w->f_subject = new_ml_form_text (w->pool, form);
+  ml_widget_set_property (w->f_subject, "form.text.size", 50);
+  if (subject)
+    ml_form_input_set_value (w->f_subject, subject);
+  ml_form_layout_pack (tbl, "Title: ", w->f_subject);
+
+  /* Body. */
+  w->f_body = new_ml_form_textarea (w->pool, form, 5, 50);
+  if (body)
+    ml_form_input_set_value (w->f_body, body);
+  ml_form_layout_pack (tbl, "Notes: ", w->f_body);
+
+  /* Submit buttons. */
+  buttons = new_ml_flow_layout (w->pool);
+  w->edit = new_ml_form_submit (w->pool, form, "Edit");
+  ml_flow_layout_pack (buttons, w->edit);
+  w->delete = new_ml_form_submit (w->pool, form, "Delete");
+  ml_flow_layout_pack (buttons, w->delete);
+
+  ml_form_layout_pack (tbl, 0, buttons);
+
+  /* Pack everything together. */
+  ml_form_pack (form, tbl);
+  ml_window_pack (win, form);
+}
+
+static void
+edit_note (ml_session session, void *vw)
+{
+  ml_calendar_notes w = (ml_calendar_notes) vw;
+  int userid;
+  int edit_pressed, delete_pressed;
+  const char *s, *subject, *body;
+  int i, resid;
+  db_handle dbh;
+  st_handle sth;
+
+  /* Only logged-in users can post. */
+  /* XXX Resource-level security - later. */
+  userid = ml_session_userid (session);
+  if (!userid)
+    {
+      ml_error_window (w->pool, session,
+                      "You must be logged in to change "
+                      "events in the calendar.",
+                      ML_DIALOG_CLOSE_BUTTON);
+      return;
+    }
+
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  /* Which submit button was pressed? */
+  s = ml_form_input_get_value (w->edit);
+  edit_pressed = s && strlen (s) > 0;
+
+  s = ml_form_input_get_value (w->delete);
+  delete_pressed = s && strlen (s) > 0;
+
+  assert ((edit_pressed || delete_pressed) &&
+         !(edit_pressed && delete_pressed));
+
+  /* Delete button pressed? */
+  if (delete_pressed)
+    {
+      sth = st_prepare_cached
+       (dbh,
+        "delete from ml_calendar_events "
+        "where id = ? and resid in (@)",
+        DBI_INT, DBI_VECTOR_INT);
+      st_execute (sth, w->eventid, _ml_calendar_get_calendars (w->calendar));
+
+      /* Commit change to the database. */
+      db_commit (dbh);
+      put_db_handle (dbh);
+
+      /* Print confirmation page. */
+      ml_ok_window (w->pool, session,
+                   "That event was deleted.",
+                   ML_DIALOG_CLOSE_BUTTON|ML_DIALOG_CLOSE_RELOAD_OPENER);
+      update_notes (w);
+      return;
+    }
+
+  /* Verify that the user has at least given a subject line. If not, just
+   * return, which redisplays the form.
+   */
+  subject = ml_form_input_get_value (w->f_subject);
+  if (!subject || strlen (subject) == 0)
+    return;
+
+  /* Verify the remaining fields are in range. */
+  i = ml_form_select_get_selection (w->f_resid);
+  if (i < 0 || i >= vector_size (w->f_resids))
+    return;
+
+  /* Pull the other fields from the form. */
+  body = ml_form_input_get_value (w->f_body);
+  vector_get (w->f_resids, i, resid);
+
+  /* Update the database. */
+  sth = st_prepare_cached
+    (dbh,
+     "update ml_calendar_events "
+     "set resid = ?, subject = ?, body = ?, "
+     "last_modified_date = current_timestamp "
+     "where id = ? and resid in (@)",
+     DBI_INT, DBI_STRING, DBI_STRING, DBI_INT, DBI_VECTOR_INT);
+  st_execute (sth,
+             resid,
+             subject, body,
+             w->eventid, _ml_calendar_get_calendars (w->calendar));
+
+  /* Commit change to the database. */
+  db_commit (dbh);
+  put_db_handle (dbh);
+
+  /* Print confirmation page. */
+  ml_ok_window (w->pool, session,
+               "That event was edited.",
+               ML_DIALOG_CLOSE_BUTTON|ML_DIALOG_CLOSE_RELOAD_OPENER);
+  update_notes (w);
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_calendar_notes w = (ml_calendar_notes) vw;
+
+  ml_widget_repaint (w->top, session, windowid, io);
+}
diff --git a/calendar/ml_calendar_notes.h b/calendar/ml_calendar_notes.h
new file mode 100644 (file)
index 0000000..f2b819f
--- /dev/null
@@ -0,0 +1,34 @@
+/* Monolith calendar widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_calendar_notes.h,v 1.3 2003/02/22 15:34:27 rich Exp $
+ */
+
+#ifndef ML_CALENDAR_NOTES_H
+#define ML_CALENDAR_NOTES_H
+
+#include <monolith.h>
+
+#include "ml_calendar.h"
+
+struct ml_calendar_notes;
+typedef struct ml_calendar_notes *ml_calendar_notes;
+
+extern ml_calendar_notes new_ml_calendar_notes (pool, ml_session, const char *conninfo, ml_calendar w);
+extern void ml_calendar_notes_set_date (ml_calendar_notes, int yyyy, int mm, int dd);
+
+#endif /* ML_CALENDAR_NOTES_H */
diff --git a/calendar/ml_calendar_visible.c b/calendar/ml_calendar_visible.c
new file mode 100644 (file)
index 0000000..1048d13
--- /dev/null
@@ -0,0 +1,141 @@
+/* Monolith calendar widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_calendar_visible.c,v 1.4 2003/02/22 15:34:27 rich Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <vector.h>
+
+#include <pthr_dbi.h>
+
+#include <monolith.h>
+#include <ml_widget.h>
+#include <ml_flow_layout.h>
+#include <ml_text_label.h>
+
+#include "ml_calendar_lib.h"
+#include "ml_calendar_visible.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations calendar_visible_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_calendar_visible
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_session session;          /* Current session. */
+  const char *conninfo;                /* Database connection. */
+  ml_calendar calendar;                /* Parent calendar. */
+  ml_flow_layout flow;         /* List of calendars. */
+};
+
+static pool cal_vis_pool;
+static ml_text_label bullet;
+
+static void calendar_visible_init (void) __attribute__ ((constructor));
+static void calendar_visible_stop (void) __attribute__ ((destructor));
+
+static void
+calendar_visible_init ()
+{
+  cal_vis_pool = new_subpool (global_pool);
+  bullet = new_ml_text_label (cal_vis_pool, "*");
+}
+
+static void
+calendar_visible_stop ()
+{
+  delete_pool (cal_vis_pool);
+}
+
+ml_calendar_visible
+new_ml_calendar_visible (pool pool, ml_session session, const char *conninfo,
+                        ml_calendar calendar)
+{
+  ml_calendar_visible w = pmalloc (pool, sizeof *w);
+
+  w->ops = &calendar_visible_ops;
+  w->pool = pool;
+  w->session = session;
+  w->conninfo = conninfo;
+  w->calendar = calendar;
+
+  return w;
+}
+
+extern void
+ml_calendar_visible_update (ml_calendar_visible w)
+{
+  vector resids = _ml_calendar_get_calendars (w->calendar);
+  db_handle dbh;
+  st_handle sth;
+  int resid;
+  const char *resname;
+  ml_text_label lbl;
+
+  w->flow = new_ml_flow_layout (w->pool);
+
+  /* Get the list of calendars from the database. */
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  sth = st_prepare_cached
+    (dbh,
+     "select c.resid, r.name "
+     "  from ml_calendar c, ml_resources r "
+     " where c.resid in (@) and c.resid = r.resid "
+     " order by 1",
+     DBI_VECTOR_INT);
+  st_execute (sth, resids);
+
+  st_bind (sth, 0, resid, DBI_INT);
+  st_bind (sth, 1, resname, DBI_STRING);
+
+  while (st_fetch (sth))
+    {
+      ml_flow_layout_pack (w->flow, bullet);
+      lbl = new_ml_text_label (w->pool, resname);
+      ml_widget_set_property (lbl, "color", _ml_calendar_colour (resid));
+      ml_flow_layout_pack (w->flow, lbl);
+    }
+
+  put_db_handle (dbh);
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_calendar_visible w = (ml_calendar_visible) vw;
+
+  ml_widget_repaint (w->flow, session, windowid, io);
+}
diff --git a/calendar/ml_calendar_visible.h b/calendar/ml_calendar_visible.h
new file mode 100644 (file)
index 0000000..fa142b5
--- /dev/null
@@ -0,0 +1,34 @@
+/* Monolith calendar widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_calendar_visible.h,v 1.4 2003/02/22 15:34:27 rich Exp $
+ */
+
+#ifndef ML_CALENDAR_VISIBLE_H
+#define ML_CALENDAR_VISIBLE_H
+
+#include <monolith.h>
+
+#include "ml_calendar.h"
+
+struct ml_calendar_visible;
+typedef struct ml_calendar_visible *ml_calendar_visible;
+
+extern ml_calendar_visible new_ml_calendar_visible (pool, ml_session, const char *conninfo, ml_calendar w);
+extern void ml_calendar_visible_update (ml_calendar_visible w);
+
+#endif /* ML_CALENDAR_VISIBLE_H */
diff --git a/chat/.cvsignore b/chat/.cvsignore
new file mode 100644 (file)
index 0000000..1964f99
--- /dev/null
@@ -0,0 +1 @@
+build-*
\ No newline at end of file
diff --git a/chat/README.chatbot-api b/chat/README.chatbot-api
new file mode 100644 (file)
index 0000000..2f092f9
--- /dev/null
@@ -0,0 +1,168 @@
+The 'chatbot' API
+-----------------
+
+The chatbot API allows chatbots to interact in chatrooms as if they
+were ordinary users. Potentially it could also be used in the future
+to allow Java applets on the client to post and read messages.
+
+Chatbots are designed to be written in a scripting language such as
+perl with libwww-perl. The API has been designed so that the scripting
+language doesn't necessarily have to be multi-threaded (to deal with
+multiple chatrooms), although you could use a threaded language if
+you wanted to.
+
+Another concern is that the API has been designed so that it shouldn't
+drop messages, except under abnormally huge loads.
+
+The API consists of a single shared object script, normally located at
+http://server/so-bin/chatbot.so
+
+The 'fn' parameter controls the function requested. This parameter is
+mandatory, and the possible values are explained below.
+
+Room and message identification
+-------------------------------
+
+Each room has a unique resource ID (resid).
+
+Each message in a room has a unique message ID. At a particular point
+in time, a room contains a window of continuous message IDs:
+
+  room contains: [ first_message  . . . . . . . . last_message ]
+
+The last_message is the most recently posted message.
+
+The first_message ID is retrieved when you list all rooms (see
+fn=rooms below). You can then retrieve this message using the fn=wait
+function. To retrieve the next message, increment the message ID and
+call fn=wait again, and so on until you reach the last message. At
+this point, incrementing the message ID to last_message+1 and calling
+fn=wait causes the CGI call to wait until another message is posted.
+
+As you will see below, fn=wait also allows you to retrieve messages
+across many different rooms.
+
+Authentication
+--------------
+
+Chatbots should send an authentication cookie. Currently, because of
+the no-security implementation (see below), they should send the
+following cookie:
+
+name: userid
+value: chatbot's userid
+
+Currently if no cookie is sent, the chatbot appears as an anonymous
+user. In future we will be adding proper, secure cookies and an
+authentication mechanism.
+
+Error handling
+--------------
+
+Error handling is through the normal HTTP mechanism so if you, for
+example, request a room which doesn't exist, then you will get a 500
+Internal Server Error, with an attached text/plain content section
+containing the error message.
+
+Security
+--------
+
+There is no security in the chat server at the present time.
+
+API functions
+-------------
+
+fn=rooms
+--------
+
+Returns a list of the rooms available, one per line, in the following
+format:
+
+resid msgid name_of_the_room
+
+resid : the room resource ID (ie. unique number)
+msgid : the message ID of the first available message in the room
+
+fn=wait
+-------
+
+In its simplest form, this function allows you to retrieve messages
+posted in a room, possibly waiting for a message to be posted.
+
+If the room has resid 1, and the first message ID (from the fn=rooms
+call) is 3, then calling:
+
+fn=wait&rooms=1&msgids=1:3
+
+reads message ID 3 from resid 1. To read the next message, increment
+the message ID, thus:
+
+fn=wait&rooms=1&msgids=1:4
+fn=wait&rooms=1&msgids=1:5
+etc.
+
+Eventually one of these calls will block until a message is posted
+in the room.
+
+More normally, you will use the fn=wait call to monitor several rooms
+at once. For this, you need to construct:
+
+rooms  : comma-separated list of resids
+msgids : comma-separated list of resid:msgid pairs
+
+Each resid:msgid pair is the next expected message ID in each room.
+You should start with the first message ID from each room, and
+increment the msgid _only_ when you receive that msgid in that room.
+For example:
+
+fn=wait&rooms=1,2&msgids=1:5,2:1
+
+This API call may return one or more messages. Each message is on its
+own line with the following format:
+
+resid msgid msgtype [optional fields depending on msgtype ...]
+
+resid   : the room ID
+msgid   : the message ID
+msgtype : one of the following message types:
+
+msgtype 'posted' (an ordinary posted message):
+
+  resid msgid posted hh:mm userid [username] text_of_the_message
+
+  hh:mm    : the time the message was posted
+  userid   : the user ID (0 = the anonymous user)
+  username : for non-anonymous users, the name of the user
+  text_of_the_message : the message text as typed by the user
+
+msgtype 'enter'/'leave' (a user entered or left the room):
+
+  resid msgid enter userid [username]
+
+  userid   : the user ID (0 = the anonymous user)
+  username : for non-anonymous users, the name of the user
+
+msgtype 'blank'/'gone': clients should ignore all messages of these types.
+
+For example:
+
+1 5 posting 10:45 2 rich This is a really insightful comment
+1 6 posting 10:46 0 This is an anonymous posting
+1 7 enter 3 bob
+1 8 leave 0
+2 1 blank
+
+Chatbots should ignore lines that they don't understand (eg. if the
+line doesn't start with a number).
+
+fn=post
+-------
+
+Post a message to a room. The following parameters are required:
+
+fn=post&room=resid&text=message
+
+resid : the room ID
+text  : the text of the message in smart-text format
+
+Note that chatbots reads back their own messages.
diff --git a/chat/bots/chatbotlib.pl b/chat/bots/chatbotlib.pl
new file mode 100644 (file)
index 0000000..c1294f8
--- /dev/null
@@ -0,0 +1,82 @@
+#!/usr/bin/perl -w -T
+
+=pod
+
+=head1 NAME
+
+chatbotlib.pl - Library for accessing chatbots
+
+=head1 SYNOPSIS
+
+require "/usr/share/monolith-chat/bots/chatbotlib.pl";
+
+cb_init (\@ARGV);
+@rooms = cb_get_all_rooms ();
+$msg = cb_wait_message (rooms => \@rooms, msgtypes => CB_POSTING|CB_ENTER);
+cb_post_message (room => $roomid, text => "the message");
+
+=over 4
+
+=cut
+
+use strict;
+
+use LWP;
+use LWP::UserAgent;
+
+use vars qw($_cb_server $_cb_userid);
+
+=item cb_init (\@ARGV);
+
+Initialize the chatbot library. This parses and removes parameters
+from the global C<@ARGV> which control the behaviour of the chatbot.
+These parameters are:
+
+=over 4
+
+=item -s http://server/so-bin/chatbot.so
+
+The URL of the C<chatbox.so> shared object script on the
+server. This defaults to C<http://localhost/so-bin/chatbox.so>
+
+=item -u userid
+
+The user ID to use. The default is 0 (anonymous).
+
+=back
+
+=cut
+
+sub cb_init
+  {
+    # This is a reference to the global @ARGV array.
+    my $ARGV = shift;
+
+    for (my $i = 0; $i < @$ARGV-1; ++$i)
+      {
+       if ($ARGV->[$i] eq '-s')
+         {
+           ($_, $_cb_server) = splice @$ARGV, $i, 2;
+         }
+       elsif ($ARGV->[$i] eq '-u')
+         {
+           ($_, $_cb_userid) = splice @$ARGV, $i, 2;
+         }
+      }
+  }
+
+=item @rooms = cb_get_all_rooms ()
+
+Return a list of all rooms.
+
+=cut
+
+
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2003 Richard W.M. Jones.
+
+=cut
diff --git a/chat/bots/kharmabot.pl b/chat/bots/kharmabot.pl
new file mode 100644 (file)
index 0000000..8c9fd6e
--- /dev/null
@@ -0,0 +1,108 @@
+#!/usr/bin/perl -w -T
+
+use strict;
+
+# Configure this to the location of your kharma database.
+my $kharma_db = "/var/tmp/kharma.db";
+
+# This should probably be a real Perl module ...
+require "/usr/share/monolith-chat/bots/chatbotlib.pl";
+
+cb_init (\@ARGV);
+
+# Get a list of all rooms.
+my @rooms = cb_get_all_rooms ();
+
+# Kharma.
+my %kharma;
+dbmopen %kharma, $kharma_db, 0666;
+
+for (;;)
+{
+    # Get next posting message.
+    my $msg = cb_wait_message (rooms => \@rooms,
+                              msgtypes => CB_POSTING);
+    my $userid = $msg->{userid};
+    my $resid = $msg->{resid};
+    my $text = $msg->{text};
+
+    # Only ordinary users are allowed to change kharma.
+    if ($userid > 0)
+    {
+       # Does it contain [some word]++ or [some word]--?
+       change_kharma ($_, 1, $resid, $userid)
+           foreach (map { lc } ($text =~ m/\b([-\w.]+)\+\+\b/g));
+       change_kharma ($_, -1, $resid, $userid)
+           foreach (map { lc } ($text =~ m/\b([-\w.]+)--\b/g));
+    }
+
+    # User is asking for the ranking of a particular word.
+    if ($text =~ /^rank\s+([-\w.]+)\s*$/i)
+    {
+       my $word = lc $1;
+
+       if (! exists $kharma{$word})
+       {
+           cb_post_message
+               (room => $resid,
+                text => "$word has no kharma");
+       }
+       else
+       {
+           my $k = $kharma{$word};
+
+           if ($k == 1)
+           {
+               cb_post_message
+                   (room => $resid,
+                    text => "$word has 1 point of kharma");
+           }
+           else
+           {
+               cb_post_message
+                   (room => $resid,
+                    text => "$word has $k points of kharma");
+           }
+       }
+
+    }
+}
+
+sub change_kharma
+{
+    my $word = shift;
+    my $change = shift;
+    my $resid = shift;
+    my $userid = shift;
+
+    if (! exists $kharma{$word})
+    {
+       $kharma{$word} = 0;
+    }
+
+    # XXX Check that users don't change kharma too frequently.
+    $kharma{$word} += $change;
+    my $k = $kharma{$word};
+
+    # Garbage collected?
+    if ($k == 0)
+    {
+       delete $kharma{$word};
+
+       cb_post_message
+           (room => $resid,
+            text => "$word has no kharma and has been garbage collected");
+    }
+    elsif ($k == 1)
+    {
+       cb_post_message
+           (room => $resid,
+            text => "$word has 1 point of kharma");
+    }
+    else
+    {
+       cb_post_message
+           (room => $resid,
+            text => "$word has $k points of kharma");
+    }
+}
diff --git a/chat/chatbot.c b/chat/chatbot.c
new file mode 100644 (file)
index 0000000..ed363b8
--- /dev/null
@@ -0,0 +1,280 @@
+/* Monolith chatbot API.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: chatbot.c,v 1.5 2003/02/22 15:34:27 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <vector.h>
+#include <hash.h>
+#include <pstring.h>
+#include <pthr_pseudothread.h>
+#include <rws_request.h>
+#include <monolith.h>
+
+#include "chatroom.h"
+
+/* Define some RFC-compliant dates to represent past and future. */
+#define DISTANT_PAST   "Thu, 01 Dec 1994 16:00:00 GMT"
+#define DISTANT_FUTURE "Sun, 01 Dec 2030 16:00:00 GMT"
+
+/* Headers which are sent to defeat caches. */
+#define NO_CACHE_HEADERS "Cache-Control", "must-revalidate", "Expires", DISTANT_PAST, "Pragma", "no-cache"
+
+#define CRLF "\r\n"
+
+static int bad_request_error (pool pool, http_request http_request, io_handle io, const char *text);
+static vector parse_rooms_param (pool pool, const char *rooms);
+static int do_rooms (pool pool, http_request http_request, io_handle io, cgi cgi);
+static int do_wait (pool pool, http_request http_request, io_handle io, cgi cgi);
+static int do_post (pool pool, http_request http_request, io_handle io, cgi cgi);
+
+static const char *conninfo = 0;
+
+/* Please see the file README.chatbot-api for the full API. */
+int
+handle_request (rws_request rq)
+{
+  pool pool = pth_get_pool (current_pth);
+  http_request http_request = rws_request_http_request (rq);
+  io_handle io = rws_request_io (rq);
+  cgi cgi;
+  const char *fn;
+  int close;
+
+  /* Get the conninfo string. */
+  if (!conninfo)
+    conninfo = rws_request_cfg_get_string (rq, "chatbot database", "");
+
+  /* Parse CGI parameters. */
+  cgi = new_cgi (pool, http_request, io);
+
+  /* What function was selected? */
+  fn = cgi_param (cgi, "fn");
+  if (!fn)
+    return bad_request_error (pool, http_request, io,
+                             "fn parameter is missing");
+
+  if (strcasecmp (fn, "rooms") == 0)
+    close = do_rooms (pool, http_request, io, cgi);
+  else if (strcasecmp (fn, "wait") == 0)
+    close = do_wait (pool, http_request, io, cgi);
+  else if (strcasecmp (fn, "post") == 0)
+    close = do_post (pool, http_request, io, cgi);
+  else
+    close = bad_request_error (pool, http_request, io, "unknown fn parameter");
+
+  return close;
+}
+
+static int
+do_rooms (pool pool, http_request http_request, io_handle io, cgi cgi)
+{
+  db_handle dbh;
+  st_handle sth;
+  int resid;
+  const char *name;
+  http_response http_response;
+  int close;
+
+  /* XXX Future: access control. */
+  dbh = get_db_handle (conninfo, DBI_THROW_ERRORS);
+  sth = st_prepare_cached
+    (dbh,
+     "select c.resid, r.name from ml_chat_rooms c, ml_resources r "
+     " where c.resid = r.resid "
+     " order by 1");
+  st_execute (sth);
+
+  st_bind (sth, 0, resid, DBI_INT);
+  st_bind (sth, 1, name, DBI_STRING);
+
+  http_response = new_http_response (pool, http_request, io, 200, "OK");
+  http_response_send_headers (http_response,
+                             /* Content type. */
+                             "Content-Type", "text/plain",
+                             NO_CACHE_HEADERS,
+                             /* End of headers. */
+                             NULL);
+  close = http_response_end_headers (http_response);
+
+  if (http_request_is_HEAD (http_request)) return close;
+
+  while (st_fetch (sth))
+    io_fprintf (io, "%d 0 %s" CRLF, resid, name); /* XXX msgid */
+
+  return close;
+}
+
+static int
+do_wait (pool pool, http_request http_request, io_handle io, cgi cgi)
+{
+  const char *rooms, *msgids;
+  vector chatrooms;
+  hash next_msgids;
+  message msg;
+  chatroom room;
+  http_response http_response;
+  int i, resid, next_msgid, close;
+  const char *response;
+
+  rooms = cgi_param (cgi, "rooms");
+  if (!rooms)
+    return bad_request_error (pool, http_request, io,
+                             "rooms parameter is missing");
+
+  chatrooms = parse_rooms_param (pool, rooms);
+  if (!chatrooms)
+    return bad_request_error (pool, http_request, io,
+                             "rooms parameter empty or could not be parsed");
+
+  msgids = cgi_param (cgi, "msgids");
+  if (!msgids)
+    return bad_request_error (pool, http_request, io,
+                             "msgids parameter is missing");
+
+  next_msgids = parse_msgids_param (pool, msgids);
+  if (!next_msgids)
+    return bad_request_error (pool, http_request, io,
+                             "msgids parameter could not be parsed");
+
+  /* Verify that there is a next_msgid for each room. */
+  for (i = 0; i < vector_size (chatrooms); ++i)
+    {
+      vector_get (chatrooms, i, resid);
+      if (!hash_exists (next_msgids, resid))
+       return bad_request_error (pool, http_request, io,
+                                 "msgids parameter missing room");
+    }
+
+  /* Wait for the next message in all chatrooms. */
+  if (chatroom_get_message_multiple (chatrooms, next_msgids, &msg, &room) == 0)
+    {
+      /* Message found in room. */
+      hash_get (next_msgids, resid, next_msgid);
+      resid = chatroom_get_resid (room);
+
+      response = psprintf (pool, "%d %d ", resid, next_msgid);
+    }
+  else
+    {
+      /* Message gone in room. */
+      hash_get (next_msgids, resid, next_msgid);
+      resid = chatroom_get_resid (room);
+
+      response = psprintf (pool, "%d %d gone", resid, next_msgid);
+    }
+
+
+
+
+
+
+
+
+
+
+}
+
+static int
+do_post (pool pool, http_request http_request, io_handle io, cgi cgi)
+{
+  abort ();
+}
+
+/* Parse the rooms parameter (a list of resids) and return a vector
+ * of chatroom objects. If the parsing fails for some reason, return
+ * NULL.
+ */
+static vector
+parse_rooms_param (pool pool, const char *rooms)
+{
+  vector chatrooms;
+  vector rooms_v;
+  db_handle dbh;
+  st_handle sth;
+  int i;
+
+  /* Split up the list of rooms. */
+  rooms_v = pstrcsplit (pool, rooms, ',');
+  if (vector_size (rooms_v) == 0)
+    return bad_request_error (pool, http_request, io,
+                             "rooms parameter is empty");
+
+  dbh = get_db_handle (conninfo, DBI_THROW_ERRORS);
+  sth = st_prepare_cached
+    (dbh, "select 1 from ml_chat_rooms where resid = ?", DBI_INT);
+
+  chatrooms = new_vector (pool, chatroom);
+
+  /* Resolve each chatroom object.
+   * XXX Future: access control for each room.
+   */
+  for (i = 0; i < vector_size (rooms_v); ++i)
+    {
+      const char *room;
+      int resid;
+      chatroom cr;
+
+      vector_get (rooms_v, i, room);
+      if (sscanf (room, "%d", &resid) != 1) return 0;
+
+      st_execute (sth, resid);
+
+      if (!st_fetch (sth)) return 0; /* Room does not exist. */
+      st_finish (sth);
+
+      cr = get_chatroom (dbh, resid);
+      vector_push_back (chatrooms, cr);
+    }
+
+  return chatrooms;
+}
+
+static int
+bad_request_error (pool pool, http_request http_request, io_handle io,
+                  const char *text)
+{
+  http_response http_response;
+  int close;
+
+  http_response = new_http_response (pool, http_request, io,
+                                    500, "Internal server error");
+  http_response_send_headers (http_response,
+                             /* Content type. */
+                             "Content-Type", "text/plain",
+                             NO_CACHE_HEADERS,
+                             /* End of headers. */
+                             NULL);
+  close = http_response_end_headers (http_response);
+
+  if (http_request_is_HEAD (http_request)) return close;
+
+  /* XXX Escaping. */
+  io_fputs (text, io);
+
+  return close;
+}
diff --git a/chat/chatroom.c b/chat/chatroom.c
new file mode 100644 (file)
index 0000000..1251b3e
--- /dev/null
@@ -0,0 +1,408 @@
+/* Monolith chatroom.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: chatroom.c,v 1.6 2003/02/22 15:34:27 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <hash.h>
+#include <pstring.h>
+
+#include <pthr_dbi.h>
+#include <pthr_wait_queue.h>
+
+#include <monolith.h>
+
+#include "chatroom.h"
+
+static void chatroom_init (void) __attribute__((constructor));
+static void chatroom_stop (void) __attribute__((destructor));
+
+static pool cr_pool;           /* Pool for global allocations. */
+static hash rooms;             /* Maps resid -> room structure. */
+
+/* Chatbots wait on multiple rooms, and so as well as the per-room
+ * message_wq we also have this global message_wq. Chatbots wait on
+ * this and then check each room to find out which has new messages.
+ */
+static wait_queue message_wq;  /* Global message wait queue. */
+
+struct chatroom
+{
+  int resid;                   /* Resource ID. */
+  vector messages;             /* Messages posted to the room, in order. */
+  wait_queue message_wq;       /* Sleep on this when waiting for messages. */
+
+  /* Threads is a hash of pth -> struct thread. It records each thread
+   * currently in the message display loop.
+   */
+  hash threads;
+
+  /* Users is a hash of userid -> number of times that user is registered
+   * in a struct thread (above). Element 0 is special because it records
+   * the number of anonymous users.
+   */
+  hash users;
+
+  wait_queue users_wq;         /* Sleep on this when waiting enter/leave. */
+};
+
+struct thread
+{
+  pool sp;                     /* Subpool of thread pool. */
+  int userid;                  /* User ID associated with this thread. */
+  const char *username;                /* Username (NULL for anonymous). */
+};
+
+static void
+chatroom_init ()
+{
+  cr_pool = new_subpool (global_pool);
+  rooms = new_hash (cr_pool, int, struct chatroom *);
+  message_wq = new_wait_queue (cr_pool);
+}
+
+static void
+chatroom_stop ()
+{
+  delete_pool (cr_pool);
+  cr_pool = 0;                 /* Force segfault. */
+  rooms = 0;
+  message_wq = 0;
+}
+
+chatroom
+get_chatroom (db_handle dbh, int resid)
+{
+  st_handle sth;
+  const char *body, *username, *original_ip;
+  struct dbi_timestamp posted_date;
+  int authorid;
+  chatroom room;
+  message m;
+
+  /* Return the object from the hash, if found. */
+  if (hash_get (rooms, resid, room)) return room;
+
+  /* Not found, so we need to create the object. */
+  room = pmalloc (cr_pool, sizeof *room);
+  room->resid = resid;
+  room->messages = new_vector (cr_pool, message);
+  room->message_wq = new_wait_queue (cr_pool);
+  room->threads = new_hash (cr_pool, pseudothread, struct thread);
+  room->users = new_hash (cr_pool, int, int);
+  room->users_wq = new_wait_queue (cr_pool);
+
+  /* Pull out the recent messages for this room from the log. */
+  sth = st_prepare_cached
+    (dbh,
+     "select g.body, g.posted_date, g.author, u.username, g.original_ip "
+     "  from ml_chat_log g left outer join ml_users u on g.author = u.userid "
+     " where g.resid = ? and "
+     "       g.posted_date >= current_timestamp - interval '1 hour'"
+     " order by g.posted_date",
+     DBI_INT);
+  st_execute (sth, resid);
+
+  st_bind (sth, 0, body, DBI_STRING);
+  st_bind (sth, 1, posted_date, DBI_TIMESTAMP);
+  st_bind (sth, 2, authorid, DBI_INT);
+  st_bind (sth, 3, username, DBI_STRING);
+  st_bind (sth, 4, original_ip, DBI_STRING);
+
+  while (st_fetch (sth))
+    {
+      m = new_message_posting (cr_pool, posted_date.hour, posted_date.min,
+                              authorid, authorid ? username : 0, original_ip,
+                              body);
+
+      vector_push_back (room->messages, m);
+    }
+
+  /* Save the object. */
+  hash_insert (rooms, resid, room);
+
+  /* Return it. */
+  return room;
+}
+
+int
+chatroom_first_message_index (const chatroom r)
+{
+  /* This naive implementation keeps all messages around forever, hence ... */
+  return 0;
+}
+
+int
+chatroom_last_message_index (const chatroom r)
+{
+  /* Note that this returns last message index + 1. */
+  return vector_size (r->messages);
+}
+
+message
+chatroom_get_message (const chatroom r, int index,
+                     void (*pre_sleep) (ml_session, void *data),
+                     ml_session session, void *data)
+{
+  message m;
+
+  if (index < 0)
+    return 0;
+
+ again:
+  if (index < vector_size (r->messages))
+    {
+      vector_get (r->messages, index, m);
+      return m;
+    }
+
+  /* The caller is asking for a future message, so now we go to sleep until
+   * it appears.
+   */
+
+  /* Call the pre_sleep function, if any. */
+  if (pre_sleep)
+    {
+      pre_sleep (session, data);
+      pre_sleep = 0;           /* Don't go to sleep again. */
+    }
+
+  /* Go to sleep. */
+  ml_session_release_lock (session);
+  wq_sleep_on (r->message_wq);
+  ml_session_acquire_lock (session);
+
+  /* Try to get the message again. */
+  goto again;
+}
+
+extern int chatroom_get_message_multiple (const vector rooms, const hash next_msgids, message *msg_rtn, chatroom *room_rtn);
+
+void
+chatroom_post (chatroom r, ml_session session, const char *conninfo,
+              const char *body)
+{
+  db_handle dbh;
+  st_handle sth;
+  int userid, msgid;
+  const char *username, *original_ip;
+  struct dbi_timestamp posted_date;
+  message m;
+
+  /* Get the currently logged in user. */
+  userid = ml_session_userid (session);
+
+  dbh = get_db_handle (conninfo, DBI_THROW_ERRORS);
+
+  /* Insert the message into the database. */
+  sth = st_prepare_cached
+    (dbh,
+     "insert into ml_chat_log (resid, body, author, original_ip) "
+     "values (?, ?, ?, ?)",
+     DBI_INT, DBI_STRING, DBI_INT_OR_NULL, DBI_STRING);
+  st_execute (sth, r->resid, body, userid,
+             ml_session_get_peernamestr (session));
+
+  /* Get the serial number of the message. */
+  msgid = st_serial (sth, "ml_chat_log_id_seq");
+
+  /* Fetch back the message, which will now have the default fields (in
+   * particular, the posting date) prefilled.
+   */
+  sth = st_prepare_cached
+    (dbh,
+     "select g.posted_date, u.username, g.original_ip "
+     "  from ml_chat_log g left outer join ml_users u on g.author = u.userid "
+     " where g.id = ?",
+     DBI_INT);
+  st_execute (sth, msgid);
+
+  st_bind (sth, 0, posted_date, DBI_TIMESTAMP);
+  st_bind (sth, 1, username, DBI_STRING);
+  st_bind (sth, 2, original_ip, DBI_STRING);
+
+  if (!st_fetch (sth))
+    pth_die (psprintf (ml_session_pool (session),
+                      "missing message ID: %d", msgid));
+
+  /* Save the new message. */
+  m = new_message_posting (cr_pool, posted_date.hour, posted_date.min,
+                          userid, userid ? username : 0, original_ip,
+                          body);
+
+  vector_push_back (r->messages, m);
+
+  /* Wake up anyone waiting for the message. */
+  wq_wake_up (r->message_wq);
+  wq_wake_up (message_wq);
+
+  /* Commit changes to the database. */
+  db_commit (dbh);
+  put_db_handle (dbh);
+}
+
+void
+chatroom_spam_blank (pool thread_pool)
+{
+  pool pool = new_subpool (thread_pool);
+  vector resids = hash_keys_in_pool (rooms, pool);
+  int i;
+
+  for (i = 0; i < vector_size (resids); ++i)
+    {
+      message m;
+      chatroom r;
+      int resid;
+
+      /* Get the room structure. */
+      vector_get (resids, i, resid);
+      hash_get (rooms, resid, r);
+
+      /* Send a blank message to this room. */
+      m = new_message_blank (cr_pool);
+      vector_push_back (r->messages, m);
+
+      /* Wake up anyone waiting for the message. */
+      wq_wake_up (r->message_wq);
+      wq_wake_up (message_wq);
+    }
+
+  delete_pool (pool);
+}
+
+static void leave_room (void *vr);
+
+void
+chatroom_enter (chatroom r, int userid, const char *username)
+{
+  pool sp;
+  struct thread thread;
+  int count;
+  message m;
+
+  /* Create a subpool of the _thread_ pool, so that if the thread dies
+   * unexpectedly we can catch it.
+   */
+  sp = new_subpool (pth_get_pool (current_pth));
+  pool_register_cleanup_fn (sp, leave_room, r);
+
+  /* Update threads hash. */
+  thread.userid = userid;
+  thread.username = username ? pstrdup (sp, username) : 0;
+  thread.sp = sp;
+  hash_insert (r->threads, current_pth, thread);
+
+  /* Update users hash. */
+  if (hash_get (r->users, userid, count))
+    count++;
+  else
+    count = 1;
+  hash_insert (r->users, userid, count);
+
+  /* Someone has entered the room, so wake up sleepers. */
+  wq_wake_up (r->users_wq);
+
+  if (count == 1 || userid == 0)
+    {
+      /* Also post a message in the room. */
+      m = new_message_enter (cr_pool, username);
+      vector_push_back (r->messages, m);
+
+      /* Wake up anyone waiting for the message. */
+      wq_wake_up (r->message_wq);
+      wq_wake_up (message_wq);
+    }
+}
+
+void
+chatroom_leave (chatroom r)
+{
+  struct thread thread;
+
+  /* If this fails, you probably tried to call chatroom_leave twice. */
+  assert (hash_get (r->threads, current_pth, thread));
+
+  /* This calls leave_room. */
+  delete_pool (thread.sp);
+}
+
+static void
+leave_room (void *vr)
+{
+  chatroom r = (chatroom) vr;
+  struct thread thread;
+  int count;
+  message m;
+
+  assert (hash_get (r->threads, current_pth, thread));
+  assert (hash_erase (r->threads, current_pth));
+
+  /* Update the users hash. */
+  assert (hash_get (r->users, thread.userid, count));
+  if (count > 1)
+    {
+      count--;
+      hash_insert (r->users, thread.userid, count);
+    }
+  else
+    {
+      count--;
+      assert (hash_erase (r->users, thread.userid));
+    }
+
+  /* Someone has left the room, so wake up sleepers. */
+  wq_wake_up (r->users_wq);
+
+  if (count == 0 || thread.userid == 0)
+    {
+      /* Also post a message in the room. */
+      m = new_message_leave (cr_pool, thread.username);
+      vector_push_back (r->messages, m);
+
+      /* Wake up anyone waiting for the message. */
+      wq_wake_up (r->message_wq);
+      wq_wake_up (message_wq);
+    }
+}
+
+const hash
+chatroom_users (chatroom r)
+{
+  return r->users;
+}
+
+void
+chatroom_wait_enter_leave_event (chatroom r, ml_session session)
+{
+  ml_session_release_lock (session);
+  wq_sleep_on (r->users_wq);
+  ml_session_acquire_lock (session);
+}
diff --git a/chat/chatroom.h b/chat/chatroom.h
new file mode 100644 (file)
index 0000000..7921d4e
--- /dev/null
@@ -0,0 +1,89 @@
+/* Monolith chatroom.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: chatroom.h,v 1.6 2003/02/22 15:34:27 rich Exp $
+ */
+
+#ifndef CHATROOM_H
+#define CHATROOM_H
+
+#include <pool.h>
+#include <hash.h>
+#include <vector.h>
+#include <pthr_dbi.h>
+#include <monolith.h>
+
+#include "message.h"
+
+struct chatroom;
+typedef struct chatroom *chatroom;
+
+/* Get (or create) the shared chatroom object for resource resid. */
+extern chatroom get_chatroom (db_handle dbh, int resid);
+
+/* Get the resid of a particular chatroom. */
+extern int chatroom_get_resid (const chatroom r);
+
+/* Get the index of the first and last message + 1 available in the room.
+ * NB. The '+ 1'.
+ */
+extern int chatroom_first_message_index (const chatroom r);
+extern int chatroom_last_message_index (const chatroom r);
+
+/* Get a message. If you try to get a message < first index, then this
+ * function returns NULL. If you try to get a message >= last index, then
+ * this function sleeps until the message becomes available. Before
+ * going to sleep, the function "pre_sleep (session, data)" is called.
+ */
+extern message chatroom_get_message (const chatroom r, int index, void (*pre_sleep) (ml_session, void *data), ml_session session, void *data);
+
+/* Get the next message across several chatrooms. This is used by the
+ * chatbot interface. 'rooms' is a vector of chatroom objects.
+ * 'next_msgids' is a hash from chatroom resid -> message ID
+ * (equivalent to the 'index' parameter to the chatroom_get_message
+ * function above). On success, the function sets *msg_rtn and
+ * *room_rtn and returns 0. On failure (basically, message < first
+ * index as above), then this sets *room_rtn only and returns -1.
+ */
+extern int chatroom_get_message_multiple (const vector rooms, const hash next_msgids, message *msg_rtn, chatroom *room_rtn);
+
+/* Post a message into the chatroom. Assumes the current time and the
+ * current user identity from the session.
+ */
+extern void chatroom_post (chatroom r, ml_session session, const char *conninfo, const char *body);
+
+/* Post a blank message to all chatrooms. */
+extern void chatroom_spam_blank (pool thread_pool);
+
+/* Enter and leave the chatroom. The chatroom object uses these functions
+ * to maintain a list of who is in the room at a moment in time.
+ */
+extern void chatroom_enter (chatroom r, int userid, const char *username);
+extern void chatroom_leave (chatroom r);
+
+/* Get the current list of users. This returns a hash of userid -> count
+ * where count is the number of times this user is in the room. In
+ * particular, element 0 is the number of anonymous users in the room.
+ */
+extern const hash chatroom_users (chatroom r);
+
+/* This function is used to wait for changes in the list of users. It
+ * sleeps until a user enters or leaves the room and then returns.
+ */
+extern void chatroom_wait_enter_leave_event (chatroom r, ml_session session);
+
+#endif /* CHATROOM_H */
diff --git a/chat/lib.c b/chat/lib.c
new file mode 100644 (file)
index 0000000..4dcff0c
--- /dev/null
@@ -0,0 +1,71 @@
+/* Monolith chat library.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: lib.c,v 1.3 2003/02/22 15:34:27 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pool.h>
+#include <pthr_dbi.h>
+#include <pthr_iolib.h>
+
+#include <monolith.h>
+
+void
+chat_fill_buffer (io_handle io, ml_session session, const char *conninfo)
+{
+  int userid;
+  int bufsize;
+  db_handle dbh;
+  st_handle sth;
+  int i;
+
+  /* Calculate the size of the buffer. Anonymous users always get
+   * the default, which is 4096 bytes. For other users, we go to the
+   * database. (We always need to go to the database to cope with the
+   * case of multiple rws instances ... doh!)
+   */
+  userid = ml_session_userid (session);
+  if (userid == 0)
+    bufsize = 4096;
+  else
+    {
+      dbh = get_db_handle (conninfo, DBI_THROW_ERRORS);
+
+      sth = st_prepare_cached
+       (dbh,
+        "select fill_buffer from ml_chat_userprefs where userid = ?",
+        DBI_INT);
+      st_execute (sth, userid);
+
+      st_bind (sth, 0, bufsize, DBI_INT);
+
+      if (!st_fetch (sth)) bufsize = 4096; /* Default. */
+
+      put_db_handle (dbh);
+    }
+
+  /* Send the fill buffer back to the user. */
+  for (i = 0; i < bufsize; ++i)
+    io_fputc (' ', io);
+
+  io_fflush (io);
+}
diff --git a/chat/lib.h b/chat/lib.h
new file mode 100644 (file)
index 0000000..1d3681a
--- /dev/null
@@ -0,0 +1,30 @@
+/* Monolith chat library.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: lib.h,v 1.3 2003/02/22 15:34:28 rich Exp $
+ */
+
+#ifndef LIB_H
+#define LIB_H
+
+#include <pthr_dbi.h>
+#include <pthr_iolib.h>
+#include <monolith.h>
+
+void chat_fill_buffer (io_handle io, ml_session session, const char *conninfo);
+
+#endif /* LIB_H */
diff --git a/chat/message.c b/chat/message.c
new file mode 100644 (file)
index 0000000..833d3d4
--- /dev/null
@@ -0,0 +1,215 @@
+/* Monolith message.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: message.c,v 1.4 2003/02/22 12:56:26 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+#include <pthr_iolib.h>
+
+#include <monolith.h>
+#include <ml_smarttext.h>
+
+#include "message.h"
+
+enum message_type {
+  type_posting, type_enter, type_leave, type_blank
+};
+
+struct message
+{
+  enum message_type type;      /* Type of message. */
+  union {
+    struct {
+      int hh, mm;              /* Time of posting. */
+      const char *body;                /* Message body (turned to HTML). */
+      int authorid;            /* Author user ID (0 == anonymous). */
+      const char *original_ip; /* IP address as a string. */
+    } posting;
+  } u;
+  const char *username;                /* Printable username (null == anonymous). */
+};
+
+message
+new_message_posting (pool pool, int hh, int mm,
+                    int authorid, const char *username,
+                    const char *original_ip,
+                    const char *body)
+{
+  message m = pmalloc (pool, sizeof *m);
+
+  m->type = type_posting;
+  m->u.posting.hh = hh;
+  m->u.posting.mm = mm;
+  m->u.posting.authorid = authorid;
+  m->u.posting.original_ip = pstrdup (pool, original_ip);
+  m->u.posting.body = ml_smarttext_to_html (pool, body);
+  m->username = username ? pstrdup (pool, username) : 0;
+
+  return m;
+}
+
+message
+new_message_enter (pool pool, const char *username)
+{
+  message m = pmalloc (pool, sizeof *m);
+
+  m->type = type_enter;
+  m->username = username ? pstrdup (pool, username) : 0;
+
+  return m;
+}
+
+message
+new_message_leave (pool pool, const char *username)
+{
+  message m = pmalloc (pool, sizeof *m);
+
+  m->type = type_leave;
+  m->username = username ? pstrdup (pool, username) : 0;
+
+  return m;
+}
+
+message
+new_message_blank (pool pool)
+{
+  message m = pmalloc (pool, sizeof *m);
+
+  m->type = type_blank;
+
+  return m;
+}
+
+static const char *
+colour_for_user (int userid)
+{
+  switch (userid & 7)
+    {
+    case 0: return "#990033";  /* Also used for anonymous users. */
+    case 1: return "#ff0066";
+    case 2: return "#cc00cc";
+    case 3: return "#6600cc";
+    case 4: return "#3333ff";
+    case 5: return "#003366";
+    case 6: return "#009999";
+    case 7: return "#ff9900";
+    }
+  /*NOTREACHED*/
+  return 0;
+}
+
+void
+message_show (const message m, io_handle io)
+{
+  const char *username;
+  const char *colour;
+  const char *anon = "anonymous";
+
+  switch (m->type)
+    {
+    case type_posting:
+      /* Construct the username. */
+      if (m->u.posting.authorid)
+       /* Ugh. We promise not to modify it! */
+       username = m->username;
+      else
+       {
+         int len = strlen (anon) + strlen (m->u.posting.original_ip) + 2;
+         char *u;
+
+         u = alloca (len);
+         snprintf (u, len, "%s@%s", anon, m->u.posting.original_ip);
+         username = u;
+       }
+
+      /* Pick a colour. */
+      colour = colour_for_user (m->u.posting.authorid);
+
+      /* Display it. */
+      /* XXX Escaping! */
+      io_fprintf (io,
+                 "<span class=\"ml_chat_time\">%02d:%02d</span> "
+                 "<span class=\"ml_chat_username\">&lt;%s&gt;</span> "
+                 "<span class=\"ml_chat_message\" "
+                 "style=\"color: %s\">%s</span><br>",
+                 m->u.posting.hh, m->u.posting.mm, username, colour,
+                 m->u.posting.body);
+      break;
+
+    case type_enter:
+      if (m->username)
+       {
+         /* XXX Escaping! */
+         io_fprintf (io,
+                     "<span class=\"ml_chat_enterleave\">"
+                     "<span class=\"ml_chat_username\">%s</span> "
+                     "has entered the room</span><br>",
+                     m->username);
+       }
+      else
+       {
+         io_fprintf (io,
+                     "<span class=\"ml_chat_enterleave\">"
+                     "an anonymous user has entered the room</span><br>");
+       }
+
+      break;
+
+    case type_leave:
+      if (m->username)
+       {
+         /* XXX Escaping! */
+         io_fprintf (io,
+                     "<span class=\"ml_chat_enterleave\">"
+                     "<span class=\"ml_chat_username\">%s</span> "
+                     "has left the room</span><br>",
+                     m->username);
+       }
+      else
+       {
+         io_fprintf (io,
+                     "<span class=\"ml_chat_enterleave\">"
+                     "an anonymous user has left the room</span><br>");
+       }
+
+      break;
+
+    case type_blank:
+      io_fputs (" ", io);
+
+      break;
+    }
+}
diff --git a/chat/message.h b/chat/message.h
new file mode 100644 (file)
index 0000000..696262b
--- /dev/null
@@ -0,0 +1,43 @@
+/* Monolith message.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: message.h,v 1.2 2003/02/22 12:56:26 rich Exp $
+ */
+
+#ifndef MESSAGE_H
+#define MESSAGE_H
+
+#include <pthr_iolib.h>
+#include <monolith.h>
+
+struct message;
+typedef struct message *message;
+
+/* Create a new ordinary message posting. */
+extern message new_message_posting (pool pool, int hh, int mm, int authorid, const char *username, const char *original_ip, const char *body);
+
+/* Create an enter or leave message. */
+extern message new_message_enter (pool pool, const char *username);
+extern message new_message_leave (pool pool, const char *username);
+
+/* Blank messages (sent by the ping thread). */
+extern message new_message_blank (pool pool);
+
+/* Render the message. */
+extern void message_show (const message m, io_handle io);
+
+#endif /* MESSAGE_H */
diff --git a/chat/messages_pane.c b/chat/messages_pane.c
new file mode 100644 (file)
index 0000000..9247579
--- /dev/null
@@ -0,0 +1,163 @@
+/* Monolith chat messages pane widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: messages_pane.c,v 1.6 2003/02/22 15:34:28 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+
+#include <pthr_iolib.h>
+#include <pthr_pseudothread.h>
+
+#include <monolith.h>
+#include <ml_widget.h>
+
+#include "chatroom.h"
+#include "lib.h"
+
+#include "messages_pane.h"
+
+#define NR_RECENT_MESSAGES 20
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations messages_pane_ops =
+  {
+    repaint: repaint
+  };
+
+struct messages_pane
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_session session;          /* Current session. */
+  const char *conninfo;                /* Database connection. */
+  chatroom room;               /* The chatroom. */
+};
+
+messages_pane
+new_messages_pane (pool pool, ml_session session, const char *conninfo,
+                  chatroom room)
+{
+  messages_pane w = pmalloc (pool, sizeof *w);
+
+  w->ops = &messages_pane_ops;
+  w->pool = pool;
+  w->session = session;
+  w->conninfo = conninfo;
+  w->room = room;
+
+  return w;
+}
+
+struct pre_sleep_args
+{
+  messages_pane w;
+  io_handle io;
+};
+
+/* This function is called just before the repaint loop goes to sleep
+ * waiting for the next message to be posted. The purpose of the function
+ * therefore is to flush any output buffers and drive the data back to
+ * the user through any proxies along the way (as far as this is
+ * possible).
+ */
+static void
+pre_sleep (ml_session session, void *vargs)
+{
+  struct pre_sleep_args *args = (struct pre_sleep_args *) vargs;
+  messages_pane w = args->w;
+
+  /* Scroll the window up. */
+  io_fputs ("<script language=\"javascript\"><!--\n"
+            "window.scrollBy(0,200000);\n"
+            "setTimeout ('window.scrollBy(0,200000);', 1000);\n"
+            "//--></script>\n", args->io);
+
+  /* Send the fill buffer. */
+  chat_fill_buffer (args->io, session, w->conninfo);
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  messages_pane w = (messages_pane) vw;
+  int i, userid;
+  const char *username = 0;
+  message m;
+  db_handle dbh;
+  st_handle sth;
+  struct pre_sleep_args args;
+
+  /* Prepare the arguments for the pre_sleep function above. */
+  args.w = w;
+  args.io = io;
+
+  userid = ml_session_userid (session);
+  if (userid)
+    {
+      /* Resolve the username. */
+      dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+      sth = st_prepare_cached
+       (dbh,
+        "select username from ml_users where userid = ?",
+        DBI_INT);
+      st_execute (sth, userid);
+      st_bind (sth, 0, username, DBI_STRING);
+      st_fetch (sth);
+
+      put_db_handle (dbh);
+    }
+
+  /* Tell the chatroom object that we are in the room. We remain in the
+   * room as long as this repaint function is being run. This does
+   * mean that if we logout and login as someone else (or just login)
+   * then the chatroom object won't know who we are until we reopen this
+   * frame, but don't worry about it.
+   */
+  chatroom_enter (w->room, userid, username);
+
+  /* Start by displaying some recent messages (if there are any). */
+  i = chatroom_last_message_index (w->room) - NR_RECENT_MESSAGES;
+
+  for (;; i++)
+    {
+      /* Display the next message. */
+      m = chatroom_get_message (w->room, i, pre_sleep, session, &args);
+      if (!m) continue;
+
+      /* Note that messages know how to render themselves. */
+      message_show (m, io);
+    }
+
+  chatroom_leave (w->room);
+}
diff --git a/chat/messages_pane.h b/chat/messages_pane.h
new file mode 100644 (file)
index 0000000..1559e8e
--- /dev/null
@@ -0,0 +1,33 @@
+/* Monolith chat messages pane widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: messages_pane.h,v 1.4 2003/02/22 15:34:28 rich Exp $
+ */
+
+#ifndef MESSAGES_PANE_H
+#define MESSAGES_PANE_H
+
+#include <monolith.h>
+
+#include "chatroom.h"
+
+struct messages_pane;
+typedef struct messages_pane *messages_pane;
+
+extern messages_pane new_messages_pane (pool, ml_session, const char *conninfo, chatroom room);
+
+#endif /* MESSAGES_PANE_H */
diff --git a/chat/ml_chat_button.c b/chat/ml_chat_button.c
new file mode 100644 (file)
index 0000000..ff7a6a1
--- /dev/null
@@ -0,0 +1,52 @@
+/* Monolith chat button.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_chat_button.c,v 1.4 2003/02/22 15:34:28 rich Exp $
+ */
+
+#include "config.h"
+
+#include <pool.h>
+#include <pstring.h>
+
+#include <monolith.h>
+#include <ml_button.h>
+#include "ml_chat_window.h"
+
+#include "ml_chat_button.h"
+
+ml_button
+new_ml_chat_button (pool pool, ml_session session, const char *conninfo,
+                   const char *resname, const char *text)
+{
+  ml_button b;
+  struct ml_chat_window_args *args;
+
+  b = new_ml_button (pool, text);
+  if (!b) return 0;
+
+  args = pmalloc (pool, sizeof *args);
+  args->conninfo = conninfo;
+  args->resname = resname;
+
+  ml_button_set_callback (b, ml_chat_window, session, args);
+  ml_button_set_popup (b,
+                      psprintf (pool, "ml_chat_window_%s", resname));
+  ml_button_set_popup_size (b, 550, 650);
+
+  return b;
+}
diff --git a/chat/ml_chat_button.h b/chat/ml_chat_button.h
new file mode 100644 (file)
index 0000000..affbbe9
--- /dev/null
@@ -0,0 +1,44 @@
+/* Monolith chat button.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_chat_button.h,v 1.3 2003/02/22 15:34:28 rich Exp $
+ */
+
+#ifndef ML_CHAT_BUTTON_H
+#define ML_CHAT_BUTTON_H
+
+#include <monolith.h>
+#include <ml_button.h>
+
+/* Function: ml_chat_button - monolith chat button
+ *
+ * This is a wrapper around @code{new_ml_button} and
+ * @code{ml_chat_window} which creates a "chat button". When pressed,
+ * this button pops up a new window containing a chat window.
+ *
+ * @code{pool} is the pool for allocations. @code{session} is the
+ * current session. @code{conninfo} is the database connection.
+ * @code{resname} is the name of the chat room. @code{text} is the
+ * text to display on the button.
+ *
+ * This returns an ordinary @code{ml_button} widget.
+ *
+ * See also: @ref{new_ml_button(3)}, @ref{ml_chat_window(3)}.
+ */
+extern ml_button new_ml_chat_button (pool pool, ml_session session, const char *conninfo, const char *resname, const char *text);
+
+#endif /* ML_CHAT_WINDOW_H */
diff --git a/chat/ml_chat_window.c b/chat/ml_chat_window.c
new file mode 100644 (file)
index 0000000..d8e2e55
--- /dev/null
@@ -0,0 +1,255 @@
+/* Monolith chat window.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_chat_window.c,v 1.9 2003/02/22 15:34:28 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+
+#include <pthr_dbi.h>
+
+#include <monolith.h>
+#include <ml_widget.h>
+#include <ml_window.h>
+#include <ml_button.h>
+#include <ml_text_label.h>
+#include <ml_form.h>
+#include <ml_form_input.h>
+#include <ml_form_text.h>
+#include <ml_dialog.h>
+
+#include "messages_pane.h"
+#include "users_pane.h"
+#include "chatroom.h"
+
+#include "ml_chat_window.h"
+
+#define MAX_POSTING_LENGTH 320
+
+struct data {
+  pool pool;                   /* Pool for allocations. */
+  ml_session session;          /* Session. */
+  const char *conninfo;                /* Database connection. */
+  int resid;                   /* Resource ID of the chatroom. */
+  const char *resname;         /* Name of the chatroom. */
+  chatroom room;               /* Chatroom object. */
+  ml_form_text posted;         /* Current posted message. */
+};
+
+static void create_top_frameset (ml_session session, void *vdata);
+static void create_messages_frame (ml_session session, void *vdata);
+static void create_users_frame (ml_session session, void *vdata);
+static void create_input_frame (ml_session session, void *vdata);
+static void post_message (ml_session session, void *vdata);
+
+void
+ml_chat_window (ml_session session, void *vargs)
+{
+  struct ml_chat_window_args *args = (struct ml_chat_window_args *) vargs;
+  pool pool = ml_session_pool (session);
+  struct data *data;
+  db_handle dbh;
+  st_handle sth;
+  ml_window win;
+  vector frames;
+  struct ml_frame_description frame;
+
+  /* Initialise our own data structure. */
+  data = pmalloc (pool, sizeof *data);
+  data->pool = pool;
+  data->session = session;
+  data->conninfo = args->conninfo;
+  data->resname = pstrdup (pool, args->resname);
+
+  /* Fetch the resource ID from the database. */
+  dbh = get_db_handle (args->conninfo, DBI_THROW_ERRORS);
+
+  sth = st_prepare_cached
+    (dbh,
+     "select resid from ml_resources where name = ?", DBI_STRING);
+  st_execute (sth, args->resname);
+
+  st_bind (sth, 0, data->resid, DBI_INT);
+
+  if (!st_fetch (sth))
+    {
+      ml_error_window (pool, session,
+                      "That chat room could not be found.",
+                      ML_DIALOG_CLOSE_BUTTON);
+    }
+
+  /* Get the corresponding chatroom object. */
+  data->room = get_chatroom (dbh, data->resid);
+
+  put_db_handle (dbh);
+
+  /* Create the top-level frameset. */
+  frames = new_vector (pool, struct ml_frame_description);
+
+  frame.data = data;
+  frame.fn = create_top_frameset;
+  vector_push_back (frames, frame);
+  frame.fn = create_input_frame;
+  vector_push_back (frames, frame);
+
+  win = new_ml_frameset (session, pool, "92%,8%", "*", frames);
+}
+
+static void
+create_top_frameset (ml_session session, void *vdata)
+{
+  struct data *data = (struct data *) vdata;
+  pool pool = data->pool;
+  ml_window win;
+  vector frames;
+  struct ml_frame_description frame;
+
+  /* Create the top frameset, showing the messages on the left and the
+   * users on the right.
+   */
+  frames = new_vector (pool, struct ml_frame_description);
+
+  frame.data = vdata;
+  frame.fn = create_messages_frame;
+  vector_push_back (frames, frame);
+  frame.fn = create_users_frame;
+  vector_push_back (frames, frame);
+
+  win = new_ml_frameset (session, pool, "*", "80%,20%", frames);
+}
+
+static void
+create_messages_frame (ml_session session, void *vdata)
+{
+  struct data *data = (struct data *) vdata;
+  pool pool = data->pool;
+  ml_window win;
+  messages_pane messages_pane;
+
+  win = new_ml_window (session, pool);
+
+  messages_pane = new_messages_pane (pool, session,
+                                    data->conninfo, data->room);
+  ml_window_pack (win, messages_pane);
+}
+
+static void
+create_users_frame (ml_session session, void *vdata)
+{
+  struct data *data = (struct data *) vdata;
+  pool pool = data->pool;
+  ml_window win;
+  users_pane users_pane;
+  const char *ua;
+  int ie_bug_workaround;
+
+  win = new_ml_window (session, pool);
+
+  /* IE 4 and above has a serious bug: it cannot handle two or more
+   * frames in the same frameset which are continuously loading. Sniff
+   * for IE here and invoke the bug workaround instead.
+   */
+  ua = ml_session_user_agent (session);
+  ie_bug_workaround = ua && strstr (ua, "MSIE") != 0;
+
+  ml_window_set_refresh (win, ie_bug_workaround ? 60 : 600);
+
+  users_pane = new_users_pane (pool, session, data->conninfo, data->room,
+                              ie_bug_workaround);
+  ml_window_pack (win, users_pane);
+}
+
+static void
+create_input_frame (ml_session session, void *vdata)
+{
+  struct data *data = (struct data *) vdata;
+  pool pool = data->pool;
+  ml_window win;
+  ml_form form;
+
+  win = new_ml_window (session, pool);
+
+  /* Create the simple form which users will use to post. There's just
+   * an input widget, which browsers will submit when the user presses
+   * the [Return] key.
+   */
+  form = new_ml_form (pool);
+  ml_form_set_callback (form, post_message, session, data);
+  ml_widget_set_property (form, "method", "GET");
+  data->posted = new_ml_form_text (pool, form);
+  ml_form_text_focus (data->posted);
+  ml_widget_set_property (data->posted, "form.text.size", 55);
+  ml_widget_set_property (data->posted,
+                         "form.text.maxlength", MAX_POSTING_LENGTH);
+
+  ml_form_pack (form, data->posted);
+  ml_window_pack (win, form);
+}
+
+static void
+post_message (ml_session session, void *vdata)
+{
+  struct data *data = (struct data *) vdata;
+  //pool pool = data->pool;
+  const char *_posted_text;
+  char *text;
+
+  _posted_text = ml_form_input_get_value (data->posted);
+
+  /* Clear message box. */
+  ml_form_input_set_value (data->posted, "");
+
+  /* Ignore empty strings. */
+  if (!_posted_text || strlen (_posted_text) == 0) return;
+
+  /* Copy the string onto the stack, since we are going to trim it
+   * and so on. This is also good discipline, because we *must*
+   * duplicate the string into a safer pool for long-term storage.
+   * By copying it onto the stack, we'll catch mistakes easily.
+   */
+  text = alloca (strlen (_posted_text) + 1);
+  strcpy (text, _posted_text);
+
+  /* Ignore empty strings (#2). */
+  ptrim (text);
+  if (strlen (text) == 0) return;
+
+  /* Truncate the posted text at the maximum posting length. */
+  if (strlen (text) > MAX_POSTING_LENGTH)
+    text[MAX_POSTING_LENGTH] = '\0';
+
+  /* Insert it into the chatroom & database. */
+  chatroom_post (data->room, session, data->conninfo, text);
+}
diff --git a/chat/ml_chat_window.h b/chat/ml_chat_window.h
new file mode 100644 (file)
index 0000000..ed3a708
--- /dev/null
@@ -0,0 +1,61 @@
+/* Monolith chat window.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_chat_window.h,v 1.3 2003/02/22 15:34:28 rich Exp $
+ */
+
+#ifndef ML_CHAT_WINDOW_H
+#define ML_CHAT_WINDOW_H
+
+#include <monolith.h>
+
+/* Function: ml_chat_window - monolith chat window
+ *
+ * This is the monolith chat window. The chat window displays the
+ * contents of one chat room in an ordinary browser without requiring
+ * anything beyond HTML and some very simple Javascript.
+ *
+ * Chat isn't a normal widget because it makes complex use of frames.
+ *
+ * @code{ml_chat_window} creates a new frameset. You would normally
+ * set this function as a callback function from a popup button
+ * (see @code{new_ml_button(3)}, @code{ml_button_set_popup(3)},
+ * @code{ml_button_set_callback(3)}).
+ *
+ * @code{ml_chat_window_args} must be a pointer to a
+ * @code{struct ml_chat_window_args}. This structure has the following
+ * required fields:
+ *
+ * @code{conninfo}: The database connection.
+ *
+ * @code{resname}: The name of the chat room (in the @code{ml_resources}
+ * database table).
+ *
+ * See also: @ref{new_ml_chat_button(3)}.
+ */
+extern void ml_chat_window (ml_session, void *ml_chat_window_args);
+
+/* You must pass a pointer to this structure as the second argument
+ * to ml_chat_window.
+ */
+struct ml_chat_window_args
+{
+  const char *conninfo;                /* Database connection. */
+  const char *resname;         /* Chat room name. */
+};
+
+#endif /* ML_CHAT_WINDOW_H */
diff --git a/chat/thread.c b/chat/thread.c
new file mode 100644 (file)
index 0000000..e69c9cc
--- /dev/null
@@ -0,0 +1,98 @@
+/* Monolith chat ping thread.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: thread.c,v 1.4 2003/02/22 12:56:27 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pool.h>
+#include <pthr_pseudothread.h>
+
+#include <monolith.h>
+
+#include "chatroom.h"
+
+/* This is the chat ping thread. This thread sends out a blank message
+ * to all rooms at a periodic interval. The reason for having this thread
+ * at all is to stop proxy servers and browsers from timing out the
+ * connection to the chat server. The blank message just ensures that some
+ * more data is sent over the TCP connection.
+ *
+ * This thread also serves another useful purpose: if we lose the connection
+ * to the user because of a network failure, then the blank message will
+ * detect this too.
+ *
+ * This thread starts up as soon as the chat library (ie. libmonolithchat.so)
+ * is loaded. We take care to kill the thread if the chat library is
+ * unloaded. Because there is no way to explicitly kill a thread, we
+ * have to set a 'quit' flag, and rely on the thread reading this flag
+ * and exiting itself. There is an added complication here however. The
+ * 'quit' flag itself cannot be allocated in static memory, because this
+ * memory would be unmapped by the time the chat thread gets round to
+ * reading it. So instead we allocate a byte in global_pool for this flag.
+ */
+
+static void chat_ping_thread_init (void) __attribute__((constructor));
+static void chat_ping_thread_stop (void) __attribute__((destructor));
+static void run (void *);
+
+static pool thread_pool = 0;
+static pseudothread pth;
+static unsigned char *quit_ptr;        /* Address of the quit flag (see above). */
+
+#define INTERVAL 120           /* Seconds between messages. */
+
+static void
+chat_ping_thread_init ()
+{
+  if (thread_pool) abort ();   /* Something very wrong has happened. */
+
+  /* Allocate memory for the quit flag from global_pool - not statically
+   * because the memory would become unmapped before the thread would have
+   * a chance to read it.
+   */
+  quit_ptr = pmalloc (global_pool, sizeof (unsigned char));
+  *quit_ptr = 0;
+
+  /* Create and start up the thread. */
+  thread_pool = new_pool ();
+  pth = new_pseudothread (thread_pool, run, quit_ptr, "chat ping thread");
+  pth_start (pth);
+}
+
+static void
+chat_ping_thread_stop ()
+{
+  *quit_ptr = 1;
+}
+
+static void
+run (void *vp)
+{
+  unsigned char *quit_ptr = (unsigned char *) vp;
+
+  for (;;)
+    {
+      pth_sleep (INTERVAL);
+      if (*quit_ptr) return;
+      chatroom_spam_blank (thread_pool);
+    }
+}
diff --git a/chat/users_pane.c b/chat/users_pane.c
new file mode 100644 (file)
index 0000000..4c79cf8
--- /dev/null
@@ -0,0 +1,156 @@
+/* Monolith chat users pane widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: users_pane.c,v 1.6 2003/02/22 15:34:28 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <hash.h>
+#include <pstring.h>
+
+#include <monolith.h>
+#include <ml_widget.h>
+#include <ml_smarttext.h>
+
+#include "chatroom.h"
+#include "lib.h"
+
+#include "users_pane.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations users_pane_ops =
+  {
+    repaint: repaint
+  };
+
+struct users_pane
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_session session;          /* Current session. */
+  const char *conninfo;                /* Database connection. */
+  chatroom room;               /* The chatroom. */
+  int ie_bug_workaround;       /* Bug workaround for IE. */
+};
+
+users_pane
+new_users_pane (pool pool, ml_session session, const char *conninfo,
+               chatroom room, int ie_bug_workaround)
+{
+  users_pane w = pmalloc (pool, sizeof *w);
+
+  w->ops = &users_pane_ops;
+  w->pool = pool;
+  w->session = session;
+  w->conninfo = conninfo;
+  w->room = room;
+  w->ie_bug_workaround = ie_bug_workaround;
+
+  return w;
+}
+
+static inline void
+get_users (chatroom room, pool thread_pool,
+          int *nr_anon_rtn, vector *userids_rtn)
+{
+  hash users = chatroom_users (room);
+  const int zero = 0;
+  int count;
+
+  /* Get the number of anonymous users. */
+  if (hash_get (users, zero, count))
+    *nr_anon_rtn = count;
+  else
+    *nr_anon_rtn = 0;
+
+  /* Get the list of other users. */
+  *userids_rtn = hash_keys_in_pool (users, thread_pool);
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  users_pane w = (users_pane) vw;
+  pool thread_pool = pth_get_pool (current_pth);
+  int nr_anon;
+  vector userids;
+  int userid;
+  char *username;
+  db_handle dbh;
+  st_handle sth;
+
+  /* Get the current list of users. */
+  get_users (w->room, thread_pool, &nr_anon, &userids);
+
+  /* Resolve userids to usernames and display. */
+  if (vector_size (userids) > 0)
+    {
+      dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+      sth = st_prepare_cached
+       (dbh,
+        "select userid, username from ml_users where userid in (@) "
+        "order by 2",
+        DBI_VECTOR_INT);
+      st_execute (sth, userids);
+
+      st_bind (sth, 0, userid, DBI_INT);
+      st_bind (sth, 1, username, DBI_STRING);
+
+      while (st_fetch (sth))
+       {
+         io_fputs ("<span class=\"ml_chat_username\">", io);
+         ml_plaintext_print (io, username);
+         io_fputs ("</span><br>", io);
+       }
+
+      put_db_handle (dbh);
+    }
+
+  /* Display number of anonymous users. */
+  io_fprintf (io,
+             "%d&nbsp;<span class=\"ml_chat_username\">anonymous</span>",
+             nr_anon);
+
+  if (! w->ie_bug_workaround)
+    {
+      chat_fill_buffer (io, session, w->conninfo);
+
+      /* Wait for a change. */
+      chatroom_wait_enter_leave_event (w->room, session);
+
+      /* Send some javascript to force the browser to reload. */
+      io_fputs ("<script language=\"javascript\"><!--\n"
+               "window.location.reload ();\n"
+               "//--></script>\n",
+               io);
+    }
+}
diff --git a/chat/users_pane.h b/chat/users_pane.h
new file mode 100644 (file)
index 0000000..9dca768
--- /dev/null
@@ -0,0 +1,33 @@
+/* Monolith chat users pane widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: users_pane.h,v 1.5 2003/02/22 15:34:28 rich Exp $
+ */
+
+#ifndef USERS_PANE_H
+#define USERS_PANE_H
+
+#include <monolith.h>
+
+#include "chatroom.h"
+
+struct users_pane;
+typedef struct users_pane *users_pane;
+
+extern users_pane new_users_pane (pool, ml_session, const char *conninfo, chatroom room, int ie_bug_workaround);
+
+#endif /* USERS_PANE_H */
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..04d9db0
--- /dev/null
+++ b/configure
@@ -0,0 +1,66 @@
+#!/bin/sh -
+#
+# This is make+. Make+ is a set of scripts which enhance GNU make and
+# let you build RPMs, and other packages types with just one control
+# file. Read more at http://www.annexia.org/freeware/makeplus/
+#
+# The original author is Richard W.M. Jones <rich@annexia.org>.
+#
+# This software has been explicitly placed in the PUBLIC DOMAIN.  You
+# do not need any sort of license or agreement to use or copy this
+# software. You may also copyright this software yourself, and/or
+# relicense it under any terms you want, at any time and at no cost.
+# This allows you (among other things) to include this software with
+# other packages so that the user does not need to download and
+# install make+ separately.
+
+mp_options=""
+
+usage ()
+{
+    cat <<EOF
+./configure [--options]
+
+Installation directory options:
+  --prefix=PREFIX       Installation prefix [default: /usr/local]
+  --sysconfdir=SYSCONF  Installation prefix for configuration files
+                         [default: PREFIX/etc]
+  --localstatedir=STATE Installation prefix for writable files
+                         [default: PREFIX/var]
+
+Help options:
+  --help                Display this help and exit.
+  --print-mp-cmd        Display the make+ command and exit (use as final arg)
+EOF
+    exit 1
+}
+
+while [ $# -gt 0 ]; do
+    opt=$1 ; shift
+
+    case "$opt" in
+       --help)
+           usage
+           ;;
+       --print-mp-cmd)
+           echo "rm -f build-\*/config.mk"
+           echo "make+ $mp_options configure"
+           exit 0
+           ;;
+       --*prefix|--*dir)
+           opt=`echo $opt | sed 's/^--//'`
+           arg=$1 ; shift
+           mp_options="$mp_options $opt=$arg"
+           ;;
+       --*prefix=*|--*dir=*)
+           opt=`echo $opt | sed 's/^--//'`
+           mp_options="$mp_options $opt"
+           ;;
+       *)
+           mp_options="$mp_options $opt"
+           ;;
+    esac
+done
+
+rm -f build-*/config.mk
+make+ $mp_options configure
diff --git a/default.css b/default.css
new file mode 100644 (file)
index 0000000..65853aa
--- /dev/null
@@ -0,0 +1,546 @@
+/* Default monolith stylesheet. To use this, make sure you have configured
+ * rws to map the alias /ml-styles/ to /usr/share/rws/ml-styles
+ * (or wherever this stylesheet will be installed).
+ *
+ * $Id: default.css,v 1.27 2003/01/12 22:12:41 rich Exp $
+ */
+
+/*----- GENERAL -----*/
+
+body {                                 /* Application background colour. */
+       background-color: white;
+}
+
+/*----- BUTTONS -----*/
+
+a.ml_button {                          /* Enabled buttons. */
+       text-decoration: none;
+       font-weight: bold;
+       color: black;
+       padding: 3px 12px 3px 12px;
+       margin: 3px 3px 3px 3px;
+       border-style: outset;
+       border-color: black;
+       border-width: thin;
+}
+
+span.ml_button {                       /* Disabled buttons. */
+       text-decoration: none;
+       color: gray;
+       padding: 3px 12px 3px 12px;
+       margin: 3px 3px 3px 3px;
+       border-style: outset;
+       border-color: black;
+       border-width: thin;
+}
+
+a.ml_button_key {                      /* Enabled 'key' buttons. */
+       display: block;
+       width: 1em;
+       text-decoration: none;
+       font-weight: bold;
+       color: black;
+       padding: 3px 12px 3px 12px;
+       margin: 3px 3px 3px 3px;
+       border-style: outset;
+       border-color: black;
+       border-width: thin;
+}
+
+span.ml_button_key {                   /* Disabled 'key' buttons. */
+       display: block;
+       width: 1em;
+       text-decoration: none;
+       color: gray;
+       padding: 3px 12px 3px 12px;
+       margin: 3px 3px 3px 3px;
+       border-style: outset;
+       border-color: black;
+       border-width: thin;
+}
+
+a.ml_button_compact {                  /* Enabled 'compact' buttons. */
+       text-decoration: underline;
+       font-weight: bold;
+       color: black;
+}
+
+span.ml_button_compact {               /* Disabled 'compact' buttons. */
+       text-decoration: none;
+       color: gray;
+}
+
+a.ml_button_link {                     /* Enabled 'link' buttons. */
+       text-decoration: underline;
+       font-weight: bold;
+       color: black;
+}
+
+span.ml_button_link {                  /* Disabled 'link' buttons. */
+       text-decoration: none;
+       color: gray;
+}
+
+/*----- TOGGLE BUTTONS -----*/
+
+a.ml_toggle_button {                   /* Toggle buttons. */
+       text-decoration: none;
+       font-weight: bold;
+       color: black;
+       padding: 3px 12px 3px 12px;
+       margin: 3px 3px 3px 3px;
+       border-style: outset;
+       border-color: black;
+       border-width: 2;
+}
+
+a.ml_toggle_button_key {               /* Toggle 'key' buttons. */
+       display: block;
+       width: 1em;
+       text-decoration: none;
+       font-weight: bold;
+       color: black;
+       padding: 3px 12px 3px 12px;
+       margin: 3px 3px 3px 3px;
+       border-style: outset;
+       border-color: black;
+       border-width: 2;
+}
+
+a.ml_toggle_button_pressed {           /* Toggle buttons (pressed). */
+       text-decoration: none;
+       font-weight: bold;
+       color: black;
+       padding: 3px 12px 3px 12px;
+       margin: 3px 3px 3px 3px;
+       border-style: inset;
+       border-color: black;
+       border-width: 2;
+}
+
+a.ml_toggle_button_key_pressed {       /* Toggle 'key' buttons (pressed). */
+       display: block;
+       width: 1em;
+       text-decoration: none;
+       font-weight: bold;
+       color: black;
+       padding: 3px 12px 3px 12px;
+       margin: 3px 3px 3px 3px;
+       border-style: inset;
+       border-color: black;
+       border-width: 2;
+}
+
+/*----- BOXES -----*/
+
+span.ml_box {                          /* Box. */
+       display: block;
+       width: auto;
+       border-style: solid;
+       border-color: black;
+       border-width: thin;
+       padding: 6px 6px 6px 6px;
+       margin: 3px 3px 3px 3px;
+}
+
+/*----- FORMS -----*/
+
+input.ml_form_text {                   /* Form input text field. */
+       text-decoration: none;
+       color: black;
+       background-color: #eeeeff;
+       border-style: inset;
+       border-color: black;
+       border-width: thin;
+       padding: 3px;
+}
+
+input.ml_form_password {               /* Form input password field. */
+       text-decoration: none;
+       color: black;
+       background-color: #eeeeff;
+       border-style: inset;
+       border-color: black;
+       border-width: thin;
+       padding: 3px;
+}
+
+input.ml_form_radio {                  /* Form input radio button field. */
+       background-color: #eeeeff;
+}
+
+input.ml_form_checkbox {               /* Form input tick box field. */
+       background-color: #eeeeff;
+}
+
+select.ml_form_select {                        /* Form input select box field. */
+       text-decoration: none;
+       color: black;
+       background-color: #eeeeff;
+//     border-style: inset;
+//     border-color: black;
+//     border-width: thin;
+//     padding: 3px;
+}
+
+textarea.ml_form_textarea {            /* Form input text field. */
+       text-decoration: none;
+       color: black;
+       background-color: #eeeeff;
+       border-style: inset;
+       border-color: black;
+       border-width: thin;
+       padding: 3px;
+}
+
+input.ml_form_submit {                 /* Form submit button. */
+       text-decoration: none;
+       font-weight: bold;
+       color: black;
+       background-color: white;
+       padding: 3px 12px 3px 12px;
+       margin: 3px 3px 3px 3px;
+       border-style: outset;
+       border-color: black;
+       border-width: thin;
+}
+
+/*----- HEADINGS -----*/
+
+h1.ml_heading {                                /* Heading 1. */
+       font-size: 160%;
+       font-weight: bold;
+}
+
+h2.ml_heading {                                /* Heading 2. */
+       font-size: 140%;
+       font-weight: bold;
+}
+
+h3.ml_heading {                                /* Heading 3. */
+       font-size: 120%;
+       font-weight: bold;
+}
+
+h4.ml_heading {                                /* Heading 4. */
+       font-size: 100%;
+       font-weight: bold;
+}
+
+h5.ml_heading {                                /* Heading 5. */
+       font-size: 100%;
+       font-weight: bold;
+}
+
+h6.ml_heading {                                /* Heading 6. */
+       font-size: 100%;
+       font-weight: bold;
+}
+
+/*----- FORM LAYOUT -----*/
+
+table.ml_form_layout {                 /* Forms. */
+}
+
+table.ml_form_layout th {              /* Forms (left col). */
+       vertical-align: top;
+       text-align: right;
+       background-color: #eeeeff;
+       padding: 3px;
+}
+
+table.ml_form_layout td {              /* Forms (right col). */
+       padding: 3px;
+}
+
+/*----- SELECT LAYOUT -----*/
+
+table.ml_select_layout {               /* Select layout (outer table). */
+}
+
+table.ml_select_layout_left {          /* Select layout (inner left table). */
+       width: 100px; /* XXX */
+}
+
+table.ml_select_layout_left th {       /* Select layout (selected item). */
+       text-align: left;
+       background-color: #eeeeff;
+       padding: 3px;
+}
+
+table.ml_select_layout_left th a {     /* Select layout (selected item). */
+       font-weight: bold;
+       text-decoration: underline;
+       color: black;
+}
+
+table.ml_select_layout_left td {       /* Select layout (unselected item). */
+       text-align: left;
+       padding: 3px;
+}
+
+table.ml_select_layout_left td a {     /* Select layout (unselected item). */
+       font-weight: bold;
+       text-decoration: underline;
+       color: black;
+}
+
+/*----- MENUS -----*/
+/* This CSS is adapted from:
+ * http://www.meyerweb.com/eric/css/edge/menus/demo.html
+ */
+
+table.ml_menubar {                     /* Menubar along top of screen. */
+       border-style: outset;
+       border-color: black;
+       border-width: thin;
+       width: 100%;
+}
+
+td.ml_menubar_item {                   /* Menubar headings. */
+       font-weight: bold;
+       color: black;
+       padding: 3px 12px 3px 12px;
+       margin: 3px 3px 3px 3px
+}
+
+td.ml_menubar_item ul {                        /* Disable all menus to start. */
+       display: none;
+}
+
+td.ml_menubar_item:hover > ul {                /* On hover, enable menu. */
+       display: block;
+       background: white;
+       position: absolute;
+       top: +16px;
+       border-style: outset;
+       border-color: black;
+       border-width: thin;
+}
+
+td.ml_menubar_item li:hover > ul {     /* On hover, enable submenu. */
+       display: block;
+       background: white;
+       position: absolute;
+       top: -1px;
+       left: 70%;
+       width: 6em;
+       border-style: outset;
+       border-color: black;
+       border-width: thin;
+}
+
+td.ml_menubar_item li {                        /* Menu item. */
+       list-style-type: none;
+       padding: 3px 12px 3px 12px;
+       margin: 3px 3px 3px -30px;
+}
+
+//td.ml_menubar_item li:hover {                /* Menu item (hovering). */
+//     list-style-type: none;
+//     padding: 3px 12px 3px 12px;
+//     margin: 3px 3px 3px -30px;
+//     background-color: #eeeeff;
+//}
+
+td.ml_menubar_item li a {              /* Menu item (operational). */
+       font-weight: bold;
+       color: black;
+       text-decoration: none;
+}
+
+td.ml_menubar_item li span {           /* Menu item (not operational). */
+       font-weight: bold;
+       color: black;
+       text-decoration: none;
+}
+
+/*----- STATS APPLICATION -----*/
+
+table.ml_stats_table {                 /* Tables in stats appl. */
+       border-style: solid;
+       border-color: black;
+       border-width: 2px;
+       border-collapse: collapse;
+}
+
+table.ml_stats_table th {              /* Tables in stats appl. */
+       vertical-align: top;
+       background-color: #eeeeff;
+       border-style: solid;
+       border-color: black;
+       border-width: 1px;
+       padding: 3px;
+}
+
+table.ml_stats_table td {              /* Tables in stats appl. */
+       vertical-align: top;
+       border-style: solid;
+       border-color: black;
+       border-width: 1px;
+       padding: 3px;
+}
+
+/*----- DISCUSSION -----*/
+
+table.ml_discussion {                  /* Discussion area. */
+       width: 100%;
+}
+
+table.ml_discussion_posting {          /* Discussion posting. */
+       width: 100%;
+       margin-bottom: 0.5em;
+}
+
+th.ml_discussion_posting_read {
+       text-align: left;
+       font-weight: normal;
+       background-color: #eeeeff;
+       border-style: solid;
+       border-color: black;
+       border-width: thin;
+       padding: 3px 3px 3px 3px;
+}
+
+th.ml_discussion_posting_unread {
+       text-align: left;
+       font-weight: bold;
+       background-color: #eeeeff;
+       border-style: solid;
+       border-color: black;
+       border-width: thin;
+       padding: 3px 3px 3px 3px;
+}
+
+table.ml_discussion_panel {            /* Discussion panel actions. */
+       width: 100%;
+}
+
+table.ml_discussion_panel_actions {    /* Discussion panel actions. */
+       margin-bottom: 1em;
+}
+
+/*----- CALENDAR -----*/
+
+a.ml_calendar_day {                    /* Calendar day buttons. */
+       display: block;
+       width: 1.5em;
+       text-decoration: none;
+       font-weight: bold;
+       color: black;
+       padding-left: 3px;
+       padding-right: 3px;
+       padding-top: 1px;
+       padding-bottom: 1px;
+}
+
+a.ml_calendar_day_selected {           /* Calendar day buttons. */
+       display: block;
+       width: 1.5em;
+       text-decoration: none;
+       font-weight: bold;
+       color: black;
+       padding-left: 2px;
+       padding-right: 2px;
+       padding-top: 0px;
+       padding-bottom: 0px;
+       border-style: solid;
+       border-color: black;
+       border-width: 1px;
+}
+
+a.ml_calendar_day_events {             /* Calendar day buttons. */
+       display: block;
+       width: 1.5em;
+       text-decoration: none;
+       font-weight: bold;
+       color: red;
+       padding-left: 3px;
+       padding-right: 3px;
+       padding-top: 1px;
+       padding-bottom: 1px;
+}
+
+a.ml_calendar_day_events_selected {    /* Calendar day buttons. */
+       display: block;
+       width: 1.5em;
+       text-decoration: none;
+       font-weight: bold;
+       color: red;
+       padding-left: 2px;
+       padding-right: 2px;
+       padding-top: 0px;
+       padding-bottom: 0px;
+       border-style: solid;
+       border-color: black;
+       border-width: 1px;
+}
+
+a.ml_calendar_hour {                   /* Calendar hour buttons. */
+       text-decoration: none;
+       font-size: small;
+       font-weight: bold;
+       color: black;
+}
+
+a.ml_calendar_hour_off_peak {          /* Calendar hour buttons (off peak). */
+       text-decoration: none;
+       font-size: small;
+       font-weight: bold;
+       color: black;
+       background-color: #eeeeff;
+}
+
+table.ml_calendar_notes {              /* Calendar notes. */
+       width: 100%;
+       align: left;
+}
+
+div.ml_calendar_notes {                        /* Calendar notes. */
+       width: 100%;
+       text-align: left;
+       margin-bottom: 3pt;
+}
+
+/*----- BUG TRACKER -----*/
+
+table.ml_bugtrack_bug {                        /* Bug tracking bug table. */
+       margin-bottom: 2em;
+}
+
+table.ml_bugtrack_bug th {             /* Bug tracking bug table. */
+       text-align: right;
+       background-color: #eeeeff;
+       padding: 3px;
+}
+
+table.ml_bugtrack_bug td {             /* Bug tracking bug table. */
+       padding: 3px;
+}
+
+table.ml_bugtrack_comment {            /* Bug tracking comment. */
+       width: 100%;
+       margin-bottom: 2em;
+}
+
+table.ml_bugtrack_comment th {         /* Bug tracking comment. */
+       text-align: left;
+       margin-bottom: 2em;
+}
+
+/*----- CHAT -----*/
+
+span.ml_chat_time {                    /* Time field in chat window. */
+       color: blue;
+}
+
+span.ml_chat_username {                        /* Username field in chat window. */
+       color: red;
+}
+
+span.ml_chat_enterleave {              /* Enter/leave msgs in chat window. */
+       font-style: italic;
+}
+
+span.ml_chat_message {                 /* Message field in chat window. */
+}
diff --git a/discussion/.cvsignore b/discussion/.cvsignore
new file mode 100644 (file)
index 0000000..1964f99
--- /dev/null
@@ -0,0 +1 @@
+build-*
\ No newline at end of file
diff --git a/discussion/ml_discussion.c b/discussion/ml_discussion.c
new file mode 100644 (file)
index 0000000..765f369
--- /dev/null
@@ -0,0 +1,1372 @@
+/* Monolith discussion widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_discussion.c,v 1.13 2003/02/22 15:34:29 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <tree.h>
+#include <hash.h>
+#include <pstring.h>
+
+#include <pthr_dbi.h>
+#include <pthr_iolib.h>
+
+#include <monolith.h>
+#include <ml_window.h>
+#include <ml_widget.h>
+#include <ml_smarttext.h>
+#include <ml_button.h>
+#include <ml_form_layout.h>
+#include <ml_text_label.h>
+#include <ml_form.h>
+#include <ml_form_input.h>
+#include <ml_form_textarea.h>
+#include <ml_form_select.h>
+#include <ml_form_text.h>
+#include <ml_form_submit.h>
+#include <ml_dialog.h>
+
+#include "ml_discussion.h"
+
+#define MD_DEBUG 0
+
+#define ML_DISCUSSION_POPUP_WIN "ml_discussion_popup"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations discussion_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_discussion
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_session session;          /* Current session. */
+  const char *conninfo;                /* Database connection. */
+  int resid;                   /* Resource ID. */
+  const char *name;            /* Newsgroup (resource) name. */
+  int allow_anon;              /* Allow anonymous postings. */
+  char default_view;           /* Default view (either 1-pane or 2-pane). */
+  int first_item;              /* First item to display. */
+  int nr_items;                        /* Number of items to display on each page. */
+
+  /* In article display mode, these are the buttons that appear across the
+   * top of the widget.
+   */
+  ml_button prev, next;                /* Previous/next buttons. */
+  ml_button post;              /* Post button. */
+  ml_button mark_all_read, mark_all_unread; /* Mark all as read / unread. */
+
+  /* To avoid creating the same buttons over and over again during the
+   * repaint, cache buttons here. This is a hash of struct button_entry
+   * -> ml_button.
+   */
+  hash button_cache;
+
+  /* These are used during posting. */
+  ml_form_text subject;                /* Subject line. */
+  ml_form_textarea body;       /* Body of the posting. */
+  ml_form_select body_type;    /* Type (plain, smart, HTML). */
+};
+
+struct button_entry
+{
+  int name;
+#define BUTTON_NAME_REPLY_IN_PUBLIC   1
+#define BUTTON_NAME_REPLY_IN_PRIVATE  2
+#define BUTTON_NAME_SAVE              3
+#define BUTTON_NAME_CANCEL            4
+#define BUTTON_NAME_SUPERSEDE         5
+  int artid;
+};
+
+static inline void
+expire_old_articles (pool pool, db_handle dbh, int resid, int expiry_days)
+{
+  st_handle sth;
+  int rows, min_id;
+
+  sth = st_prepare_cached
+    (dbh,
+     "delete from ml_discussion_article "
+     "where resid = ? "
+     "and current_timestamp - posted_date >= interval ?",
+     DBI_INT, DBI_STRING);
+  rows = st_execute (sth,
+                    resid,
+                    psprintf (pool, "%d days", expiry_days));
+
+  if (rows > 0)                /* Any rows actually deleted? */
+    {
+      sth = st_prepare_cached
+       (dbh,
+        "select coalesce (min (id), 0) from ml_discussion_article "
+        "where resid = ?",
+        DBI_INT);
+      st_execute (sth, resid);
+
+      st_bind (sth, 0, min_id, DBI_INT);
+
+      sth = st_prepare_cached
+       (dbh,
+        "delete from ml_discussion_read "
+        "where resid = ? "
+        "and high <= ?",
+        DBI_INT, DBI_INT);
+      st_execute (sth, resid, min_id);
+    }
+}
+
+static void post_form (ml_session session, void *vw);
+static void mark_all_read (ml_session, void *vw);
+static void mark_all_unread (ml_session, void *vw);
+
+ml_discussion
+new_ml_discussion (pool pool, ml_session session, const char *conninfo, const char *res_name)
+{
+  ml_discussion w = pmalloc (pool, sizeof *w);
+  db_handle dbh;
+  st_handle sth;
+  char *expiry_days_str;
+
+#if MD_DEBUG
+  fprintf (stderr, "new_ml_discussion: creating new widget for %s\n",
+          res_name);
+#endif
+
+  w->ops = &discussion_ops;
+  w->pool = pool;
+  w->session = session;
+  w->conninfo = conninfo;
+  w->name = res_name;
+
+  /* Get the resource ID and a few other details. */
+  dbh = get_db_handle (conninfo, DBI_THROW_ERRORS);
+
+  sth = st_prepare_cached
+    (dbh,
+     "select r.resid, g.allow_anon, g.default_view, g.expiry_days "
+     "from ml_resources r, ml_discussion_group g "
+     "where r.name = ? "
+     "and r.resid = g.resid",
+     DBI_STRING);
+  st_execute (sth, res_name);
+
+  st_bind (sth, 0, w->resid, DBI_INT);
+  st_bind (sth, 1, w->allow_anon, DBI_BOOL);
+  st_bind (sth, 2, w->default_view, DBI_CHAR);
+  st_bind (sth, 3, expiry_days_str, DBI_STRING);
+
+  if (!st_fetch (sth)) return 0; /* Not found. */
+
+  /* Rather than running cron jobs, just expire old articles here
+   * if necessary.
+   */
+  if (expiry_days_str)
+    {
+      int expiry_days;
+
+      if (sscanf (expiry_days_str, "%d", &expiry_days) == 1 &&
+         expiry_days > 0)
+       expire_old_articles (pool, dbh, w->resid, expiry_days);
+    }
+
+  /* Be polite, put back the database handle. */
+  db_commit (dbh);
+  put_db_handle (dbh);
+
+  w->first_item = 0;
+  w->nr_items = 20;
+
+  /* Create the buttons. Previous and next buttons start disabled, but
+   * are enabled in the repaint function, if appropriate.
+   */
+  w->prev = new_ml_button (pool, "&lt;&lt;");
+  w->next = new_ml_button (pool, "&gt;&gt;");
+
+  w->post = new_ml_button (pool, "New message");
+  ml_button_set_popup (w->post, ML_DISCUSSION_POPUP_WIN);
+  ml_button_set_popup_size (w->post, 640, 480);
+  ml_button_set_callback (w->post, post_form, session, w);
+
+  w->mark_all_read = new_ml_button (pool, "Mark all read");
+  ml_button_set_callback (w->mark_all_read, mark_all_read, session, w);
+
+  w->mark_all_unread = new_ml_button (pool, "Mark all unread");
+  ml_button_set_callback (w->mark_all_unread, mark_all_unread, session, w);
+
+  /* Cache of buttons. */
+  w->button_cache = new_hash (pool, struct button_entry, ml_button);
+
+  return w;
+}
+
+static ml_button get_button (ml_discussion w, ml_session session,
+                            int name, int artid);
+static void reply_in_public_form (ml_session session, void *vargs);
+static void reply_in_private_form (ml_session session, void *vargs);
+static void save_form (ml_session session, void *vargs);
+static void cancel_form (ml_session session, void *vargs);
+static void supersede_form (ml_session session, void *vargs);
+static void prev_button (ml_session session, void *vw);
+static void next_button (ml_session session, void *vw);
+static void mark_read (ml_session, db_handle dbh, int resid, int userid, int artid);
+
+/* when we have flattened the representation of the tree into a vector,
+ * each vector element will have the following type.
+ */
+struct d_art
+{
+  int depth;                   /* 0 = top-level. */
+  int artid;                   /* Article ID. */
+};
+
+static inline void
+repaint_1pane (ml_discussion w, ml_session session,
+              const char *windowid, io_handle io,
+              db_handle dbh, int userid, vector artlist, vector artids)
+{
+  st_handle sth;
+  hash articles;
+  struct article {
+    const char *subject, *username, *body, *posted_date;
+    char body_type;
+    int userid;
+    int read;
+  };
+  int i;
+
+  assert (vector_size (artids) == vector_size (artlist));
+
+  /* Maps article ID -> article contents. */
+  articles = new_hash (w->pool, int, struct article);
+
+  /* In 1-pane mode we are going to display the full article contents. */
+  if (vector_size (artids) > 0)
+    {
+      int artid;
+      struct article art;
+
+      art.read = 0;
+
+      sth = st_prepare_cached
+       (dbh,
+        "select a.id, a.subject, u.userid, u.username, a.body, a.body_type, "
+        "a.posted_date "
+        "from ml_discussion_article a "
+        "left outer join ml_users u on a.author = u.userid "
+        "where a.resid = ? and a.id in (@)",
+        DBI_INT, DBI_VECTOR_INT);
+      st_execute (sth, w->resid, artids);
+
+      st_bind (sth, 0, artid, DBI_INT);
+      st_bind (sth, 1, art.subject, DBI_STRING);
+      st_bind (sth, 2, art.userid, DBI_INT);
+      st_bind (sth, 3, art.username, DBI_STRING);
+      st_bind (sth, 4, art.body, DBI_STRING);
+      st_bind (sth, 5, art.body_type, DBI_CHAR);
+      st_bind (sth, 6, art.posted_date, DBI_STRING);
+
+      while (st_fetch (sth))
+       hash_insert (articles, artid, art);
+    }
+
+  /* Pull out the read/unread status of each article (but not for
+   * anonymous users - they see all articles as unread).
+   */
+  if (userid)
+    {
+      int low, high;           /* Each range is [low, high-1]. */
+
+      sth = st_prepare_cached
+       (dbh,
+        "select low, high from ml_discussion_read "
+        "where resid = ? and userid = ?",
+        DBI_INT, DBI_INT);
+      st_execute (sth, w->resid, userid);
+
+      st_bind (sth, 0, low, DBI_INT);
+      st_bind (sth, 1, high, DBI_INT);
+
+      while (st_fetch (sth))
+       {
+         struct article *artp;
+         int artid;
+
+         /* Mark each article in the range [low, high-1] as read. */
+         for (artid = low; artid < high; ++artid)
+           {
+             if (hash_get_ptr (articles, artid, artp))
+               artp->read = 1;
+           }
+       }
+    }
+
+  /* Display the articles. */
+  for (i = 0; i < vector_size (artlist); ++i)
+    {
+      int artid;
+      struct d_art d_art;
+      struct article art;
+      ml_button b;
+      const char *h_class;
+
+      vector_get (artlist, i, d_art);
+      artid = d_art.artid;
+      assert (artid > 0);
+      if (!hash_get (articles, artid, art)) abort ();
+
+      io_fprintf (io,
+                 "<table class=\"ml_discussion_posting\" "
+                 "style=\"margin-left: %dem\">\n",
+                 d_art.depth * 2);
+
+      /* Unread postings appear in bold. */
+      h_class = art.read ? "read" : "unread";
+
+      /* The article subject, username, date. */
+      io_fprintf (io, "<tr><th class=\"ml_discussion_posting_%s\">",
+                 h_class);
+      ml_plaintext_print (io, art.subject);
+      io_fputs ("<br>\nby ", io);
+      ml_plaintext_print (io, (art.userid ? art.username : "anonymous"));
+      /* XXX Date formatting. */
+      io_fprintf (io, " on %s</th></tr>\n", art.posted_date);
+
+      /* The article body. */
+      io_fputs ("<tr><td>", io);
+      ml_anytext_print (io, art.body, art.body_type);
+      io_fputs ("</td></tr>", io);
+
+      /* Buttons. */
+      io_fprintf (io, "<tr><td>");
+      if (w->allow_anon || userid)
+       {
+         b = get_button (w, session, BUTTON_NAME_REPLY_IN_PUBLIC, artid);
+         ml_widget_repaint (b, session, windowid, io);
+         io_fputs ("&nbsp;", io);
+         if (userid && art.userid)
+           {
+             b = get_button (w, session, BUTTON_NAME_REPLY_IN_PRIVATE, artid);
+             ml_widget_repaint (b, session, windowid, io);
+             io_fputs ("&nbsp;", io);
+           }
+       }
+      b = get_button (w, session, BUTTON_NAME_SAVE, artid);
+      ml_widget_repaint (b, session, windowid, io);
+      io_fputs ("&nbsp;", io);
+      if (userid && userid == art.userid)
+       {
+         b = get_button (w, session, BUTTON_NAME_SUPERSEDE, artid);
+         ml_widget_repaint (b, session, windowid, io);
+         io_fputs ("&nbsp;", io);
+       }
+      if (0)                   /* XXX Administrator. */
+       {
+         b = get_button (w, session, BUTTON_NAME_CANCEL, artid);
+         ml_widget_repaint (b, session, windowid, io);
+         io_fputs ("&nbsp;", io);
+       }
+      io_fprintf (io, "</td></tr>\n");
+      io_fprintf (io, "</table>");
+
+      /* Mark article as read. */
+      if (art.read == 0) mark_read (session, dbh, w->resid, userid, artid);
+    }
+}
+
+static inline void
+repaint_2pane (ml_discussion w, ml_session session,
+              const char *windowid, io_handle io,
+              db_handle dbh, int userid, vector artlist, vector artids)
+{
+  abort (); /* XXX */
+}
+
+static inline vector
+flatten_tree (pool pool, tree node, int depth)
+{
+  int i;
+  vector v = new_vector (pool, struct d_art);
+  struct d_art d_art;
+
+  if (depth >= 0)
+    {
+      d_art.depth = depth;
+      tree_get_data (node, d_art.artid);
+      vector_push_back (v, d_art);
+    }
+
+  for (i = 0; i < tree_nr_subnodes (node); ++i)
+    {
+      tree t;
+
+      tree_get_subnode (node, i, t);
+      vector_push_back_vector (v, flatten_tree (pool, t, depth+1));
+    }
+
+  return v;
+}
+
+static inline void
+repaint_display (ml_discussion w, ml_session session,
+                const char *windowid, io_handle io)
+{
+  db_handle dbh;
+  st_handle sth;
+  int userid, i;
+  char view = w->default_view;
+  char sort_order = 'd';
+  const char *sort_col, *order, *sql;
+  int artid, parentid;
+  hash articles;
+  tree top_node;
+  const int zero = 0;
+  int more_articles;
+  vector artlist, artids;
+
+#if MD_DEBUG
+  fprintf (stderr, "ml_discussion.c: repaint_display called\n");
+#endif
+
+  /* Get a database handle. */
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+#if MD_DEBUG
+  db_set_debug (dbh, 1);
+#endif
+
+  userid = ml_session_userid (session);
+
+  if (userid)
+    {
+      /* Pull out the user preferences, so we know if we are in 1-pane
+       * or 2-pane mode.
+       */
+      sth = st_prepare_cached
+       (dbh,
+        "select view, sort_order "
+        "from ml_discussion_userprefs "
+        "where userid = ?",
+        DBI_INT);
+      st_execute (sth, userid);
+      st_bind (sth, 0, view, DBI_CHAR);
+      st_bind (sth, 1, sort_order, DBI_CHAR);
+      st_fetch (sth);
+    }
+
+  /* Pull out the article IDs and thread them by hand. Relational databases
+   * really don't handle trees at all.
+   */
+  switch (sort_order) {
+  case 'd': case 'n': case 's': order = "desc"; break;
+  default:                      order = "asc";
+  }
+  switch (sort_order) {
+  case 'd': case 'D': sort_col = "a.posted_date"; break;
+  case 'n': case 'N': sort_col = "u.username"; break;
+  default:            sort_col = "a.subject";
+  }
+  sql = psprintf (w->pool,
+                 "select a.id, coalesce (a.parent, 0), %s "
+                 "from ml_discussion_article a "
+                 "left outer join ml_users u on a.author = u.userid "
+                 "where a.resid = ? "
+                 "order by 3 %s",
+                 sort_col, order);
+  sth = st_prepare_cached (dbh, sql, DBI_INT);
+  st_execute (sth, w->resid);
+
+  st_bind (sth, 0, artid, DBI_INT);
+  st_bind (sth, 1, parentid, DBI_INT);
+
+  /* This hash maps article IDs -> tree nodes. */
+  articles = new_hash (w->pool, int, tree);
+
+  /* The top_node is empty. Children of this node are the lead
+   * articles in each thread.
+   */
+  top_node = new_tree (w->pool, int);
+  tree_set_data (top_node, zero);
+
+  hash_insert (articles, zero, top_node);
+
+  while (st_fetch (sth))
+    {
+      tree parent_node;
+      tree article_node;
+
+      /* Have we seen this parent ID node before? */
+      if (! hash_get (articles, parentid, parent_node))
+       {
+         parent_node = new_tree (w->pool, int);
+         tree_set_data (parent_node, parentid);
+         hash_insert (articles, parentid, parent_node);
+       }
+
+      /* Have we seen this article ID node before? */
+      if (! hash_get (articles, artid, article_node))
+       {
+         article_node = new_tree (w->pool, int);
+         tree_set_data (article_node, artid);
+         hash_insert (articles, artid, article_node);
+       }
+
+      /* Set the relationship between the parent node and the article node. */
+      tree_push_back (parent_node, article_node);
+    }
+
+  /* Now flatten the tree into a list, preserving depth information. */
+  artlist = flatten_tree (w->pool, top_node, -1);
+
+  /* Extract just the nodes which are going to be displayed. */
+  more_articles = vector_size (artlist) - (w->first_item + w->nr_items) > 0;
+  artlist =
+    new_subvector (w->pool, artlist, w->first_item,
+                  (more_articles ?
+                   w->first_item + w->nr_items :
+                   vector_size (artlist)));
+
+  /* Enable the previous/next buttons (if necessary). */
+  if (w->first_item > 0)
+    ml_button_set_callback (w->prev, prev_button, session, w);
+  else
+    ml_button_set_callback (w->prev, 0, session, 0);
+
+  if (more_articles)
+    ml_button_set_callback (w->next, next_button, session, w);
+  else
+    ml_button_set_callback (w->next, 0, session, 0);
+
+  /* Get the IDs of the articles we are actually going to display. */
+  artids = new_vector (w->pool, int);
+  for (i = 0; i < vector_size (artlist); ++i)
+    {
+      struct d_art d_art;
+
+      vector_get (artlist, i, d_art);
+      vector_push_back (artids, d_art.artid);
+    }
+
+  /* Print the standard buttons along the top of the widget. */
+  io_fprintf (io,
+             "<table class=\"ml_discussion\">"
+             "<tr><td align=\"left\">");
+  ml_widget_repaint (w->prev, session, windowid, io);
+  ml_widget_repaint (w->next, session, windowid, io);
+  io_fprintf (io, "</td><td align=\"right\">");
+  ml_widget_repaint (w->post, session, windowid, io);
+  ml_widget_repaint (w->mark_all_read, session, windowid, io);
+  ml_widget_repaint (w->mark_all_unread, session, windowid, io);
+  io_fprintf (io, "</td></tr>\n<tr><td colspan=\"2\">");
+
+  if (view == '1')             /* 1-pane mode. */
+    repaint_1pane (w, session, windowid, io, dbh, userid, artlist, artids);
+  else                         /* 2-pane mode. */
+    repaint_2pane (w, session, windowid, io, dbh, userid, artlist, artids);
+
+  /* Finish off the widget. */
+  io_fprintf (io, "</td></tr></table>");
+
+  /* Commit changes (in article read/unread state) back to the database. */
+  db_commit (dbh);
+
+  /* Be polite: give back the database handle. */
+#if MD_DEBUG
+  db_set_debug (dbh, 0);
+#endif
+  put_db_handle (dbh);
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_discussion w = (ml_discussion) vw;
+
+#if MD_DEBUG
+  fprintf (stderr, "ml_discussion.c: repaint called\n");
+#endif
+
+  repaint_display (w, session, windowid, io);
+}
+
+#define MR_DEBUG 0
+
+static void mark_read_error (ml_session session, int resid, int userid, char type);
+
+/* Mark the single article ID as read.
+ * Caution: This only works if the article is not already marked as read.
+ */
+static void
+mark_read (ml_session session, db_handle dbh, int resid, int userid, int artid)
+{
+  /* The method used is as follows:
+   *
+   * From the ml_discussion_article table, find the largest article number
+   * which is < artid, and the smallest article number which is > artid.
+   * Call these 'prev_artid' and 'next_artid' respectively (either may
+   * be 0, indicating no such row).
+   *
+   * Now we have:
+   *
+   * prev_artid < artid < next_artid
+   *
+   * From the ml_discussion_read table, looking only at rows which correspond
+   * to the current (resid, userid), find any rows where:
+   *
+   * prev_artid < high and high <= artid,   [type A]
+   *
+   * and rows where:
+   *
+   * artid < low and low <= next_artid.     [type B]
+   *
+   * If we found no rows, create a new row.
+   *
+   * If we found one row of type A, modify this row so high = artid + 1.
+   *
+   * If we found one row of type B, modify this row so low = artid.
+   *
+   * If we found one row of both types, merge those rows together.
+   *
+   * Anything else indicates an internal error.
+   */
+
+  st_handle sth;
+  int prev_artid, next_artid;
+  int a_fetched = 0, a_low = 0, a_high = 0,
+    b_fetched = 0, b_low = 0, b_high = 0;
+
+  if (!userid) return;         /* Ignore anonymous users. */
+
+#if MR_DEBUG
+  fprintf (stderr, "mark_read called: artid = %d\n", artid);
+#endif
+
+  sth = st_prepare_cached
+    (dbh,
+     "select max (id) from ml_discussion_article "
+     "where resid = ? and id < ?",
+     DBI_INT, DBI_INT);
+  st_execute (sth, resid, artid);
+
+  st_bind (sth, 0, prev_artid, DBI_INT);
+
+  st_fetch (sth);
+
+  sth = st_prepare_cached
+    (dbh,
+     "select min (id) from ml_discussion_article "
+     "where resid = ? and id > ?",
+     DBI_INT, DBI_INT);
+  st_execute (sth, resid, artid);
+
+  st_bind (sth, 0, next_artid, DBI_INT);
+
+  st_fetch (sth);
+
+#if MR_DEBUG
+  fprintf (stderr, "\tprev_artid = %d, next_artid = %d\n", prev_artid, next_artid);
+#endif
+
+  if (prev_artid)
+    {
+      sth = st_prepare_cached
+       (dbh,
+        "select 1, low, high from ml_discussion_read "
+        "where resid = ? and userid = ? and "
+        "? < high and high <= ?",
+        DBI_INT, DBI_INT, DBI_INT, DBI_INT);
+      st_execute (sth, resid, userid, prev_artid, artid);
+
+      st_bind (sth, 0, a_fetched, DBI_INT);
+      st_bind (sth, 1, a_low, DBI_INT);
+      st_bind (sth, 2, a_high, DBI_INT);
+
+      if (st_fetch (sth))
+       if (st_fetch (sth))
+         mark_read_error (session, resid, userid, 'A');
+    }
+
+  if (next_artid)
+    {
+      sth = st_prepare_cached
+       (dbh,
+        "select 1, low, high from ml_discussion_read "
+        "where resid = ? and userid = ? and "
+        "? < low and low <= ?",
+        DBI_INT, DBI_INT, DBI_INT, DBI_INT);
+      st_execute (sth, resid, userid, artid, next_artid);
+
+      st_bind (sth, 0, b_fetched, DBI_INT);
+      st_bind (sth, 1, b_low, DBI_INT);
+      st_bind (sth, 2, b_high, DBI_INT);
+
+      if (st_fetch (sth))
+       if (st_fetch (sth))
+         mark_read_error (session, resid, userid, 'B');
+    }
+
+#if MR_DEBUG
+  fprintf (stderr, "\ta_fetched = %d (low = %d, high = %d)\n",
+          a_fetched, a_low, a_high);
+  fprintf (stderr, "\tb_fetched = %d (low = %d, high = %d)\n",
+          b_fetched, b_low, b_high);
+#endif
+
+  /* No rows fetched: create a new row. */
+  if (!a_fetched && !b_fetched)
+    {
+#if MR_DEBUG
+      fprintf (stderr, "\tinserting low = %d, high = %d\n", artid, artid+1);
+#endif
+      sth = st_prepare_cached
+       (dbh,
+        "insert into ml_discussion_read (resid, userid, low, high) "
+        "values (?, ?, ?, ?)",
+        DBI_INT, DBI_INT, DBI_INT, DBI_INT);
+      st_execute (sth, resid, userid, artid, artid + 1);
+    }
+  /* One row of type A: modify this row so high = artid + 1. */
+  else if (a_fetched && !b_fetched)
+    {
+#if MR_DEBUG
+      fprintf (stderr, "\tmodifying low = %d, high = %d so high = %d\n",
+              a_low, a_high, artid + 1);
+#endif
+      sth = st_prepare_cached
+       (dbh,
+        "update ml_discussion_read set high = ? "
+        "where resid = ? and userid = ? and low = ? and high = ?",
+        DBI_INT, DBI_INT, DBI_INT, DBI_INT, DBI_INT);
+      st_execute (sth, artid + 1, resid, userid, a_low, a_high);
+    }
+  /* One row of type B: modify this row so low = artid. */
+  else if (!a_fetched && b_fetched)
+    {
+#if MR_DEBUG
+      fprintf (stderr, "\tmodifying low = %d, high = %d so low = %d\n",
+              b_low, b_high, artid);
+#endif
+      sth = st_prepare_cached
+       (dbh,
+        "update ml_discussion_read set low = ? "
+        "where resid = ? and userid = ? and low = ? and high = ?",
+        DBI_INT, DBI_INT, DBI_INT, DBI_INT, DBI_INT);
+      st_execute (sth, artid, resid, userid, b_low, b_high);
+    }
+  /* Two rows fetched: merge them. */
+  else
+    {
+#if MR_DEBUG
+      fprintf (stderr, "\tmerging\n");
+#endif
+      sth = st_prepare_cached
+       (dbh,
+        "delete from ml_discussion_read "
+        "where resid = ? and userid = ? and low = ? and high = ?",
+        DBI_INT, DBI_INT, DBI_INT, DBI_INT);
+      st_execute (sth, resid, userid, a_low, a_high);
+
+      sth = st_prepare_cached
+       (dbh,
+        "update ml_discussion_read set low = ? "
+        "where resid = ? and userid = ? and low = ? and high = ?",
+        DBI_INT, DBI_INT, DBI_INT, DBI_INT, DBI_INT);
+      st_execute (sth, a_low, resid, userid, b_low, b_high);
+    }
+}
+
+static void
+mark_read_error (ml_session session, int resid, int userid, char type)
+{
+  pool pool = ml_session_pool (session);
+  char *msg;
+
+  msg = psprintf
+    (pool,
+     "INTERNAL ERROR in discussion widget: error type '%c'\n"
+     "\n"
+     "Please perform the following queries on the database and send the\n"
+     "full results and error type back to your technical support contact:\n"
+     "\n"
+     "SELECT id FROM ml_discussion_article WHERE resid = %d ORDER BY 1\n"
+     "\n"
+     "SELECT low, high FROM ml_discussion_read WHERE resid = %d AND userid = %d ORDER BY 1\n",
+     type, resid, resid, userid);
+
+  pth_die (msg);
+}
+
+/* Mark all articles as read - this is very simple. */
+static void
+mark_all_read (ml_session session, void *vw)
+{
+  ml_discussion w = (ml_discussion) vw;
+  int userid = ml_session_userid (session);
+  int resid = w->resid;
+  db_handle dbh;
+  st_handle sth;
+  int max_artid;
+
+  if (!userid) return;         /* Ignore for anonymous users. */
+
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  sth = st_prepare_cached
+    (dbh,
+     "delete from ml_discussion_read where resid = ? and userid = ?",
+     DBI_INT, DBI_INT);
+  st_execute (sth, resid, userid);
+
+  /* Get the max. article ID. */
+  sth = st_prepare_cached
+    (dbh,
+     "select max(id) from ml_discussion_article where resid = ?",
+     DBI_INT);
+  st_execute (sth, resid);
+
+  st_bind (sth, 0, max_artid, DBI_INT);
+  st_fetch (sth);
+
+  if (max_artid)
+    {
+      sth = st_prepare_cached
+       (dbh,
+        "insert into ml_discussion_read (resid, userid, low, high) "
+        "values (?, ?, ?, ?)",
+        DBI_INT, DBI_INT, DBI_INT, DBI_INT);
+      st_execute (sth, resid, userid, 1, max_artid + 1);
+    }
+
+  db_commit (dbh);
+  put_db_handle (dbh);
+}
+
+/* Mark all articles as unread - this is very simple. */
+static void
+mark_all_unread (ml_session session, void *vw)
+{
+  ml_discussion w = (ml_discussion) vw;
+  int userid = ml_session_userid (session);
+  int resid = w->resid;
+  db_handle dbh;
+  st_handle sth;
+
+  if (!userid) return;         /* Ignore for anonymous users. */
+
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  sth = st_prepare_cached
+    (dbh,
+     "delete from ml_discussion_read where resid = ? and userid = ?",
+     DBI_INT, DBI_INT);
+  st_execute (sth, resid, userid);
+
+  db_commit (dbh);
+  put_db_handle (dbh);
+}
+
+struct button_args
+{
+  int artid;
+  ml_discussion w;
+};
+
+static ml_button
+get_button (ml_discussion w, ml_session session,
+           int name, int artid)
+{
+  struct button_entry entry;
+  ml_button b;
+  const char *text;
+  void (*fn) (ml_session session, void *args);
+  int popup;
+  struct button_args *args;
+
+  entry.name = name;
+  entry.artid = artid;
+
+  if (hash_get (w->button_cache, entry, b))
+    return b;
+
+  /* Create the button. */
+  switch (name)
+    {
+    case BUTTON_NAME_REPLY_IN_PUBLIC:
+      text = "Reply in public";
+      fn = reply_in_public_form;
+      popup = 1;
+      break;
+    case BUTTON_NAME_REPLY_IN_PRIVATE:
+      text = "Reply in private";
+      fn = reply_in_private_form;
+      popup = 1;
+      break;
+    case BUTTON_NAME_SAVE:
+      text = "Save";
+      //fn = save_form;
+      fn = 0; (void)save_form;
+      popup = 0;
+      break;
+    case BUTTON_NAME_CANCEL:
+      text = "Delete";
+      //fn = cancel_form;
+      fn = 0; (void)cancel_form;
+      popup = 0;
+      break;
+    case BUTTON_NAME_SUPERSEDE:
+      text = "Replace";
+      fn = supersede_form;
+      popup = 1;
+      break;
+    default:
+      abort ();
+    }
+
+  b = new_ml_button (w->pool, text);
+  args = pmalloc (w->pool, sizeof *args);
+  args->artid = artid;
+  args->w = w;
+  ml_button_set_callback (b, fn, session, args);
+
+  ml_widget_set_property (b, "button.style", "compact");
+
+  if (popup)
+    {
+      ml_button_set_popup (b, ML_DISCUSSION_POPUP_WIN);
+      ml_button_set_popup_size (b, 640, 480);
+    }
+
+  hash_insert (w->button_cache, entry, b);
+
+  return b;
+}
+
+static void _post_form (ml_discussion w, int artid, int operation);
+
+#define OP_POST 1
+#define OP_REPLY_IN_PUBLIC 2
+#define OP_REPLY_IN_PRIVATE 3
+#define OP_SUPERSEDE 4
+
+static void
+post_form (ml_session session, void *vw)
+{
+  _post_form ((ml_discussion) vw, 0, OP_POST);
+}
+
+static void
+reply_in_public_form (ml_session session, void *vargs)
+{
+  struct button_args *args = (struct button_args *) vargs;
+
+  _post_form (args->w, args->artid, OP_REPLY_IN_PUBLIC);
+}
+
+static void
+reply_in_private_form (ml_session session, void *vargs)
+{
+  struct button_args *args = (struct button_args *) vargs;
+
+  _post_form (args->w, args->artid, OP_REPLY_IN_PRIVATE);
+}
+
+static void
+save_form (ml_session session, void *vargs)
+{
+  abort ();
+}
+
+static void
+cancel_form (ml_session session, void *vargs)
+{
+  abort ();
+}
+
+static void
+supersede_form (ml_session session, void *vargs)
+{
+  struct button_args *args = (struct button_args *) vargs;
+
+  _post_form (args->w, args->artid, OP_SUPERSEDE);
+}
+
+static void
+prev_button (ml_session session, void *vw)
+{
+  ml_discussion w = (ml_discussion) vw;
+
+  w->first_item -= w->nr_items;
+  if (w->first_item < 0) w->first_item = 0;
+}
+
+static void
+next_button (ml_session session, void *vw)
+{
+  ml_discussion w = (ml_discussion) vw;
+
+  w->first_item += w->nr_items;
+}
+
+/* Structure which is used as the argument to _post. */
+struct post_args
+{
+  int artid;
+  int operation;
+  ml_discussion w;
+};
+
+static void _post (ml_session session, void *vargs);
+
+/* This function generates the new window which is used when
+ *  * posting,
+ *  * replying in public or private to, or
+ *  * superseding
+ * an article.
+ */
+static void
+_post_form (ml_discussion w, int artid, int operation)
+{
+  ml_window win;
+  ml_form form;
+  ml_form_layout tbl;
+  ml_form_submit submit;
+  db_handle dbh;
+  st_handle sth;
+  const char *art_author, *art_subject, *art_body;
+  char art_body_type;
+  struct post_args *args = pmalloc (w->pool, sizeof *args);
+
+  assert (artid || operation == OP_POST);
+
+  /* Check that the user is allowed to post. */
+  if (!w->allow_anon && !ml_session_userid (w->session))
+    {
+      ml_error_window
+       (w->pool, w->session,
+        "The administrator of this newsgroup has disallowed anonymous "
+        "postings. You need to be logged in to post to this newsgroup.",
+        ML_DIALOG_CLOSE_BUTTON);
+      return;
+    }
+
+  /* Fetch the original/parent article from the database. */
+  if (artid)
+    {
+      dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+      sth = st_prepare_cached
+       (dbh,
+        "select a.subject, a.body, a.body_type, u.username "
+        "from ml_discussion_article a "
+        "     left outer join ml_users u on a.author = u.userid "
+        "where a.id = ?",
+        DBI_INT);
+      st_execute (sth, artid);
+
+      st_bind (sth, 0, art_subject, DBI_STRING);
+      st_bind (sth, 1, art_body, DBI_STRING);
+      st_bind (sth, 2, art_body_type, DBI_CHAR);
+      st_bind (sth, 3, art_author, DBI_STRING);
+
+      if (!st_fetch (sth))
+       {
+         ml_error_window
+           (w->pool, w->session,
+            "Cannot find that article in the database. Perhaps "
+            "it has expired or been deleted by an administrator?",
+            ML_DIALOG_CLOSE_BUTTON);
+         return;
+       }
+
+      put_db_handle (dbh);
+    }
+
+  win = new_ml_window (w->session, w->pool);
+
+  /* Create and populate the form. */
+  form = new_ml_form (w->pool);
+  args->artid = artid;
+  args->operation = operation;
+  args->w = w;
+  ml_form_set_callback (form, _post, w->session, args);
+  ml_widget_set_property (form, "method", "GET");
+  tbl = new_ml_form_layout (w->pool);
+
+  /* To: line. */
+  if (operation != OP_REPLY_IN_PRIVATE)
+    ml_form_layout_pack (tbl, "To:", new_ml_text_label (w->pool, w->name));
+  else
+    ml_form_layout_pack (tbl, "To:", new_ml_text_label (w->pool, art_author));
+
+  /* Subject: line. */
+  w->subject = new_ml_form_text (w->pool, form);
+  if (operation == OP_REPLY_IN_PRIVATE || operation == OP_REPLY_IN_PUBLIC)
+    {
+      if (strncasecmp (art_subject, "Re: ", 4) == 0)
+       ml_form_input_set_value (w->subject, art_subject);
+      else
+       ml_form_input_set_value (w->subject,
+                                psprintf (w->pool, "Re: %s", art_subject));
+    }
+  else if (operation == OP_SUPERSEDE)
+    ml_form_input_set_value (w->subject, art_subject);
+  ml_widget_set_property (w->subject, "form.text.size", 50);
+  ml_form_layout_pack (tbl, "Subject:", w->subject);
+
+  /* Body text. */
+  w->body = new_ml_form_textarea (w->pool, form, 20, 60);
+  if (operation == OP_SUPERSEDE)
+    ml_form_input_set_value (w->body, art_body);
+  ml_form_layout_pack (tbl, "Body:", w->body);
+
+  /* Body type. */
+  w->body_type = new_ml_form_select (w->pool, form);
+  ml_form_select_push_back (w->body_type, "Plain text");
+  ml_form_select_push_back (w->body_type, "*Smart* text");
+  ml_form_select_push_back (w->body_type, "HTML");
+  if (operation != OP_SUPERSEDE)
+    ml_form_select_set_selection (w->body_type, 1); /* XXX From preferences. */
+  else
+    {
+      switch (art_body_type)
+       {
+       case 'p': ml_form_select_set_selection (w->body_type, 0); break;
+       case 's': ml_form_select_set_selection (w->body_type, 1); break;
+       case 'h': ml_form_select_set_selection (w->body_type, 2); break;
+       }
+    }
+  ml_form_layout_pack (tbl, 0, w->body_type);
+
+  /* Submit button. */
+  /* XXX Cancel. */
+  submit = new_ml_form_submit (w->pool, form, "Post");
+  ml_form_layout_pack (tbl, 0, submit);
+
+  ml_form_pack (form, tbl);
+  ml_window_pack (win, form);
+}
+
+static const char *clean_up_string (pool, const char *text);
+
+static void
+_post (ml_session session, void *vargs)
+{
+  struct post_args *args = (struct post_args *) vargs;
+  ml_discussion w = args->w;
+  int userid = ml_session_userid (w->session);
+  db_handle dbh;
+  st_handle sth;
+  const char *subject, *body;
+  char body_type;
+
+  /* Check that the user is allowed to post. */
+  if (!w->allow_anon && !userid)
+    {
+      ml_error_window
+       (w->pool, w->session,
+        "The administrator of this newsgroup has disallowed anonymous "
+        "postings. You need to be logged in to post to this newsgroup.",
+        ML_DIALOG_CLOSE_BUTTON);
+      return;
+    }
+
+  /* Only logged-in users may reply in private, to allow tracability of spam.*/
+  if (args->operation == OP_REPLY_IN_PRIVATE && !userid)
+    {
+      ml_error_window
+       (w->pool, w->session,
+        "You can only use the 'Reply in private' function if you "
+        "are logged in.",
+        ML_DIALOG_CLOSE_BUTTON);
+      return;
+    }
+
+  /* Check the user has put in a subject line and body. Otherwise
+   * just return which redisplays the form.
+   */
+  subject = ml_form_input_get_value (w->subject);
+  body = ml_form_input_get_value (w->body);
+  if (!subject || strlen (subject) == 0 ||
+      !body || strlen (body) == 0)
+    return;
+
+  switch (ml_form_select_get_selection (w->body_type))
+    {
+    case 0:  body_type = 'p'; break;
+    default: body_type = 's'; break;
+    case 2:  body_type = 'h'; break;
+    }
+
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  if (args->operation == OP_POST ||
+      args->operation == OP_REPLY_IN_PUBLIC)
+    {
+      /* Insert the article into the database. */
+      sth = st_prepare_cached
+       (dbh,
+        "insert into ml_discussion_article "
+        "(resid, parent, subject, author, body, body_type, original_ip) "
+        "values (?, ?, ?, ?, ?, ?, ?)",
+        DBI_INT, DBI_INT_OR_NULL, DBI_STRING, DBI_INT_OR_NULL,
+        DBI_STRING, DBI_CHAR, DBI_STRING);
+      st_execute (sth, w->resid, args->artid, subject, userid, body, body_type,
+                 ml_session_get_peernamestr (session));
+    }
+  else if (args->operation == OP_SUPERSEDE)
+    {
+      /* Replace an existing article in the database. */
+      sth = st_prepare_cached
+       (dbh,
+        "update ml_discussion_article "
+        "  set subject = ?, body = ?, body_type = ?, original_ip = ? "
+        "where id = ? and author = ?",
+        DBI_STRING, DBI_STRING, DBI_CHAR, DBI_STRING, DBI_INT, DBI_INT);
+      st_execute (sth, subject, body, body_type,
+                 ml_session_get_peernamestr (session),
+                 args->artid, userid);
+    }
+  else if (args->operation == OP_REPLY_IN_PRIVATE)
+    {
+      const char *to_username, *to_email;
+      const char *from_username, *from_email;
+      io_handle sendmail;
+      const char *sendmail_cmd = "/usr/sbin/sendmail -t -i";
+
+      /* Get the original article username and email address. */
+      sth = st_prepare_cached
+       (dbh,
+        "select a.id, u.username, u.email "
+        "from ml_discussion_article a "
+        "     left outer join ml_users u on a.author = u.userid "
+        "where a.id = ?",
+        DBI_INT);
+      st_execute (sth, args->artid);
+
+      st_bind (sth, 1, to_username, DBI_STRING);
+      st_bind (sth, 2, to_email, DBI_STRING);
+
+      if (!st_fetch (sth) || !to_username || !to_email)
+       {
+         ml_error_window
+           (w->pool, w->session,
+            "Could not retrieve the article author from the database.",
+            ML_DIALOG_CLOSE_BUTTON);
+         return;
+       }
+
+      /* Get the logged-in username and email address. */
+      sth = st_prepare_cached
+       (dbh,
+        "select username, email from ml_users where userid = ?",
+        DBI_INT);
+      st_execute (sth, userid);
+
+      st_bind (sth, 0, from_username, DBI_STRING);
+      st_bind (sth, 1, from_email, DBI_STRING);
+
+      if (!st_fetch (sth) || !from_username || !from_email)
+       {
+         ml_error_window
+           (w->pool, w->session,
+            "Could not retrieve your username or email address "
+            "from the database.",
+            ML_DIALOG_CLOSE_BUTTON);
+         return;
+       }
+
+      /* Clean up the usernames and email addresses so we can safely
+       * include them in the email.
+       */
+      to_username = clean_up_string (w->pool, to_username);
+      to_email = clean_up_string (w->pool, to_email);
+      from_username = clean_up_string (w->pool, from_username);
+      from_email = clean_up_string (w->pool, from_email);
+
+      /* Similarly clean up the subject line. */
+      subject = clean_up_string (w->pool, subject);
+
+      /* Oh dear. Sending an HTML-format email with no text alternative
+       * is *not* very clever. XXX
+       */
+      sendmail = io_popen (sendmail_cmd, "w");
+      if (!sendmail)
+       pth_die ("could not invoke sendmail");
+
+      io_fprintf (sendmail,
+                 "X-Monolith-Trace: %s %s %s\n"
+                 "From: %s <%s>\n"
+                 "To: %s <%s>\n"
+                 "Subject: %s\n"
+                 "Content-Type: text/html\n" /* Charset XXX */
+                 "\n",
+                 ml_session_get_peernamestr (session),
+                 ml_session_host_header (session),
+                 ml_session_canonical_path (session),
+                 from_username, from_email,
+                 to_username, to_email,
+                 subject);
+      io_fputs (body, sendmail);
+
+      io_pclose (sendmail);
+    }
+  else
+    {
+      abort ();                        /* Unknown operation. */
+    }
+
+  db_commit (dbh);
+  put_db_handle (dbh);
+
+  /* Confirmation page. */
+  ml_ok_window (w->pool, session,
+               "The article was sent.",
+               ML_DIALOG_CLOSE_BUTTON | ML_DIALOG_CLOSE_RELOAD_OPENER);
+}
+
+/* Remove CRs and LFs from the string. */
+static const char *
+clean_up_string (pool pool, const char *text)
+{
+  if (strpbrk (text, "\n\r"))
+    {
+      char *copy = pstrdup (pool, text);
+      char *t = copy;
+
+      while ((t = strpbrk (t, "\n\r")) != 0)
+       *t++ = ' ';
+
+      return copy;
+    }
+  else
+    return text;               /* String is safe. */
+}
diff --git a/discussion/ml_discussion.h b/discussion/ml_discussion.h
new file mode 100644 (file)
index 0000000..3807ea0
--- /dev/null
@@ -0,0 +1,49 @@
+/* Monolith discussion widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_discussion.h,v 1.4 2003/02/22 15:34:29 rich Exp $
+ */
+
+#ifndef ML_DISCUSSION_H
+#define ML_DISCUSSION_H
+
+#include <monolith.h>
+
+struct ml_discussion;
+typedef struct ml_discussion *ml_discussion;
+
+/* Function: new_ml_discussion - monolith discussion widget
+ *
+ * This is the monolith discussion widget. It is a versatile, threaded
+ * bulletin board system modelled loosely on Usenet.
+ *
+ * The schema for this widget can be found in
+ * @code{sql/ml_discussion_create.sql}.
+ *
+ * @code{new_ml_discussion} creates a new discussion widget. @code{pool}
+ * is the pool for allocations, @code{session} is the current session, and
+ * @code{dhf} is a database handle factory. @code{res_name} is the name
+ * of the newsgroup (from the @code{ml_resources} table).
+ *
+ * The function returns the discussion widget, or @code{NULL} if there
+ * was a problem, such as the newgroup not existing.
+ *
+ * See also: @ref{new_ml_discussion_panel(3)}.
+ */
+extern ml_discussion new_ml_discussion (pool, ml_session, const char *conninfo, const char *res_name);
+
+#endif /* ML_DISCUSSION_H */
diff --git a/discussion/ml_discussion_panel.c b/discussion/ml_discussion_panel.c
new file mode 100644 (file)
index 0000000..1ae6492
--- /dev/null
@@ -0,0 +1,157 @@
+/* Monolith discussion panel widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_discussion_panel.c,v 1.4 2003/02/22 15:34:29 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+
+#include <pthr_dbi.h>
+
+#include <monolith.h>
+#include <ml_window.h>
+#include <ml_widget.h>
+#include <ml_select_layout.h>
+#include <ml_multicol_layout.h>
+#include <ml_button.h>
+
+#include "ml_discussion.h"
+#include "ml_discussion_panel.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations discussion_panel_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_discussion_panel
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_session session;          /* Current session. */
+  const char *conninfo;                /* Database connection. */
+  ml_select_layout w;          /* The actual select layout. */
+};
+
+static void do_refresh (ml_session, void *vw);
+
+ml_discussion_panel
+new_ml_discussion_panel (pool pool, ml_session session, const char *conninfo)
+{
+  ml_discussion_panel w = pmalloc (pool, sizeof *w);
+  ml_multicol_layout tbl;
+  ml_button refresh_button;
+
+  w->ops = &discussion_panel_ops;
+  w->pool = pool;
+  w->session = session;
+  w->conninfo = conninfo;
+
+  /* Create the layout. */
+  w->w = new_ml_select_layout (pool, session);
+  ml_widget_set_property (w->w, "class", "ml_discussion_panel");
+
+  /* Create the action buttons. */
+  tbl = new_ml_multicol_layout (pool, 1);
+  ml_widget_set_property (tbl, "class", "ml_discussion_panel_actions");
+  refresh_button = new_ml_button (pool, "Refresh");
+  ml_button_set_callback (refresh_button, do_refresh, session, w);
+  ml_multicol_layout_pack (tbl, refresh_button);
+
+  /* Put the action buttons into the layout. */
+  ml_widget_set_property (w->w, "select_layout.top", tbl);
+
+  /* Refresh selected newsgroups. */
+  do_refresh (session, w);
+
+  return w;
+}
+
+/* XXX When called from new_ml_discussion_panel, this is OK, but when called
+ * from the button, it's a bit brutal, because it creates new copies of
+ * every widget. This is not necessary, and we could definitely be more
+ * clever here.
+ */
+static void
+do_refresh (ml_session session, void *vw)
+{
+  ml_discussion_panel w = (ml_discussion_panel) vw;
+  db_handle dbh;
+  st_handle sth;
+  int resid, unread;
+  const char *resname;
+
+  ml_select_layout_clear (w->w);
+
+  /* Pull out the list of newsgroups. */
+  /* XXX Access control. */
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  sth = st_prepare_cached
+    (dbh,
+     "select g.resid, r.name, ml_discussion_unread (?, g.resid) "
+     "  from ml_discussion_group g, ml_resources r "
+     " where g.resid = r.resid "
+     " order by 2", DBI_INT);
+  st_execute (sth, ml_session_userid (session));
+
+  st_bind (sth, 0, resid, DBI_INT);
+  st_bind (sth, 1, resname, DBI_STRING);
+  st_bind (sth, 2, unread, DBI_INT);
+
+  while (st_fetch (sth))
+    {
+      char *name;
+      ml_widget ng;
+
+      /* Create the name of this group. */
+      name = psprintf (w->pool, "%s (%d)", resname, unread);
+
+      /* Create the newsgroup widget. */
+      /* XXX Avoid the extra DB query somehow? */
+      ng = new_ml_discussion (w->pool, session, w->conninfo, resname);
+
+      /* Pack into the layout. */
+      ml_select_layout_pack (w->w, name, ng);
+    }
+
+  put_db_handle (dbh);
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_discussion_panel w = (ml_discussion_panel) vw;
+
+  if (w->w)
+    ml_widget_repaint (w->w, session, windowid, io);
+}
diff --git a/discussion/ml_discussion_panel.h b/discussion/ml_discussion_panel.h
new file mode 100644 (file)
index 0000000..f01af73
--- /dev/null
@@ -0,0 +1,51 @@
+/* Monolith discussion panel widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_discussion_panel.h,v 1.3 2003/02/22 15:34:29 rich Exp $
+ */
+
+#ifndef ML_DISCUSSION_PANEL_H
+#define ML_DISCUSSION_PANEL_H
+
+#include <monolith.h>
+
+struct ml_discussion_panel;
+typedef struct ml_discussion_panel *ml_discussion_panel;
+
+/* Function: new_ml_discussion_panel - monolith discussion panel widget
+ *
+ * The @code{ml_discussion_panel} widget displays a list of available
+ * newsgroups on the left, and a particular selected newsgroup on the
+ * right. Users can select which newsgroup to view by clicking one of
+ * the available groups on the left.
+ *
+ * The actual newsgroup is displayed using the @code{ml_discussion}
+ * widget (see @ref{new_ml_discussion(3)}.
+ *
+ * The overall layout is achieved using the @code{ml_select_layout}
+ * layout widget (see @ref{new_ml_select_layout(3)}.
+ *
+ * @code{new_ml_discussion_panel} creates a new discussion panel
+ * widget.  The pool for allocations, current session and database
+ * handle factory must be given. This function returns the panel
+ * widget, or @code{NULL} if it could not be created.
+ *
+ * See also: @ref{new_ml_discussion(3)}, @ref{new_ml_select_layout(3)}.
+ */
+extern ml_discussion_panel new_ml_discussion_panel (pool, ml_session, const char *conninfo);
+
+#endif /* ML_DISCUSSION_PANEL_H */
diff --git a/doc/hello.c b/doc/hello.c
new file mode 100644 (file)
index 0000000..16acc78
--- /dev/null
@@ -0,0 +1,35 @@
+#include <pool.h>
+
+#include <monolith.h>
+#include <ml_window.h>
+#include <ml_text_label.h>
+
+/*----- The following standard boilerplate code must appear -----*/
+
+/* Main entry point to the app. */
+static void app_main (ml_session);
+
+int
+handle_request (rws_request rq)
+{
+  return ml_entry_point (rq, app_main);
+}
+
+/*----- End of standard boilerplate code -----*/
+
+static void
+app_main (ml_session session)
+{
+  pool pool = ml_session_pool (session);
+  ml_window w;
+  ml_text_label text;
+
+  /* Create the top-level window. */
+  w = new_ml_window (session, pool);
+
+  /* Create the text widget. */
+  text = new_ml_text_label (pool, "Hello, World!");
+
+  /* Pack the text widget into the window. */
+  ml_window_pack (w, text);
+}
diff --git a/doc/index.html b/doc/index.html
new file mode 100644 (file)
index 0000000..4777ce1
--- /dev/null
@@ -0,0 +1,1617 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <title>monolith documentation index</title>
+    <style type="text/css"><!--
+      h1 {
+      text-align: center;
+      }
+      pre {
+      background-color: #eeeeff;
+      }
+      code {
+      color: green;
+      font-weight: bold;
+      }
+      span.box {
+      display: block;
+      width: auto;
+      border-style: solid;
+      border-color: black;
+      border-width: thin;
+      padding: 6px 6px 6px 6px;
+      margin: 3px 3px 3px 3px;
+      }
+      --></style>
+  </head>
+
+  <body bgcolor="#ffffff">
+    <h1>monolith documentation index</h1>
+
+    <h2>What is monolith?</h2>
+
+    <p>
+      <code>monolith</code> is an application framework which
+      generates web-based applications.  It differs from existing web
+      tools because it doesn't work in terms of "pages" and "CGI
+      scripts". Instead it allows you to build applications using
+      reusable widgets (buttons, labels, etc.)  which you arrange into
+      windows. This makes it much more like building a traditional
+      application in Windows/MFC, Java/JFC, Tcl/Tk, Motif, etc.
+    </p>
+
+    <p>
+      You can also use the basic widgets to build reusable
+      "super-widgets" which you can then embed in other applications,
+      give away or sell.  An example might be a discussion system
+      "widget" which can be placed anywhere in another application.
+    </p>
+
+    <p>
+      Of course, there are limitations in the web and browsers which
+      means that you can't do everything you might do in a normal
+      application. But you can do most things.
+    </p>
+
+    <p>
+      <code>monolith</code> programs and widgets are written in C or
+      C++ (actually I've never used C++ with <code>monolith</code>,
+      but you are welcome to try).
+    </p>
+
+    <h3>CGI vs. rws's shared object scripts vs. monolith
+    applications</h3>
+
+    <p>
+      (This section is from the <code>rws</code> documentation page).
+    </p>
+
+    <p>
+      Shared object scripts are the direct analogy to CGI scripts,
+      the only difference being that CGI scripts are usually written
+      in very high level languages like Perl and PHP, and shared
+      object scripts are loaded into the server process for efficiency.
+      (Perl CGI scripts can also be loaded into the Apache
+      server process using <code>mod_perl</code>, and this is done
+      for similar reasons of efficiency).
+    </p>
+
+    <p>
+      <code>monolith</code> programs are entire applications, the sort of
+      thing which normally would be written using dozens of
+      cooperating CGI scripts. In the case of <code>monolith</code>, however,
+      the entire application compiles down to a single <code>.so</code>
+      file which happens to be (you guessed it) a shared object script.
+    </p>
+
+    <p>
+      Imagine that you are going to write yet another web-based email
+      client. For some reason you want to write this in C (please
+      don't try this at home: I wrote one in Perl at my last job and
+      that was hard enough). Here are three possible approaches
+      using C and <code>rws</code>:
+    </p>
+
+    <ol>
+      <li>
+       <p>
+         Write forty or so shared object scripts. Each displays
+         a single frame of the application, one might generate
+         the frameset, a couple of dozen to implement specific
+         operations like emptying trash or moving a message between
+         folders.
+       </p>
+       <p>
+         This is very much the normal way of writing CGI-based
+         applications.
+       </p>
+      <li> Write a <code>monolith</code> application. This will probably be
+       in lots of C files, but will compile down and be linked
+       into a single <code>.so</code> file (eg. <code>email.so</code>)
+       which is dropped into the <code>so-bin</code> directory.
+      <li>
+       <p>
+         Write a <code>monolith</code> email super-widget. This is going
+         to exist in a shared library called
+         <code>/usr/lib/libmyemail.so</code>
+         with a corresponding header file defining the interface
+         called <code>myemail.h</code>.
+       </p>
+       <p>
+         Write a tiny <code>monolith</code> application which just instantiates
+         a window and an email widget, and embeds the email widget
+         in the window. This will compile into <code>email.so</code>
+         (it'll be very tiny) which is dropped into <code>so-bin</code>.
+       </p>
+       <p>
+         The advantage of this final approach is that you can
+         reuse the email widget in other places, or indeed sell
+         it to other <code>monolith</code> users.
+       </p>
+    </ol>
+
+    <p>
+      So <code>monolith</code> is good when you want to build applications
+      from widgets as you would if you were building a
+      Java/Swing, Windows MFC, gtk, Tcl/Tk graphical application.
+      It's also good if code re-use is important to you.
+      Shared object scripts are good when you are familiar with
+      CGI-based techniques to build websites.
+    </p>
+
+    <p>
+      Of course, the same <code>rws</code> server can serve
+      shared object scripts, multiple <code>monolith</code> applications,
+      flat files, and directory listings, all at the same time.
+    </p>
+
+    <h2>Compiling and installing monolith programs</h2>
+
+    <p>
+      <code>monolith</code> programs are C programs which compile into
+      <code>.so</code> files, called shared object scripts.
+      The <code>rws</code> webserver runs these directly.
+    </p>
+
+    <p>
+      To compile, probably the simplest thing to do is write a
+      Makefile which does:
+    </p>
+
+<pre>
+gcc -Wall -Werror -c prog.c -o prog.o
+gcc -shared -Wl,-soname,prog.so prog.o \
+               -lmonolithcore -lpthrlib -lc2 -lm -o prog.so
+</pre>
+
+    <p>
+      The last step generates the actual binary. Copy this into
+      <code>rws</code>'s
+      <code>/so-bin</code> directory, and make sure it is mode 0755
+      (<code>-rwxr-xr-x</code>).
+    </p>
+
+    <p>
+      To create an <code>/so-bin</code> directory, add this to your
+      <code>rws</code> hosts file:
+    </p>
+
+<pre>
+alias /so-bin/
+        path:           /path/to/your/so-bin
+        exec so:        1
+end alias
+</pre>
+
+    <p>
+      Now test it out by going to
+      <code>http://your.webserver/so-bin/prog.so</code>
+    </p>
+
+    <p>
+      If it doesn't work, here is a checklist before you email me:
+    </p>
+
+    <ul>
+      <li> Make sure you have put the above alias section into
+       the correct host file.
+      <li> <code>exec so</code> option is set?
+      <li> Restarted <code>rwsd</code>?
+      <li> Directory is world readable, executable (mode 0755)?
+      <li> Program is world readable, executable (mode 0755)?
+      <li> Any unresolved symbols (<code>ldd -r script.so</code>), apart
+       from the <code>rws_request_*</code> symbols which will be resolved
+       when the library is loaded into <code>rws</code>?
+      <li> Check the contents of your error_log file to see
+       if any error messages were reported.
+    </ul>
+
+    <p>
+      I have quite successfully used <code>gdb</code> on a running
+      server to debug and diagnose problems in <code>monolith</code> programs.
+      However note that by default <code>gdb</code> may have trouble
+      loading the symbol table for the <code>monolithcore</code>
+      library and your program. Use the <code>sharedlibrary monolith;
+      sharedlibrary script.so</code> command to load symbols instead.
+    </p>
+
+    <h2>Tutorial</h2>
+
+    <p>
+      This tutorial begins with the basics of <code>monolith</code>
+      application design, and then goes through some of the examples
+      which you will find in the <code>examples/</code> directory
+      in the source distribution.
+    </p>
+
+    <h3>A simple "hello, world" program</h3>
+
+    <p>
+      The simple "hello, world" program is useful because it
+      lets us (a) make sure our development environment is <em>really</em>
+      working, and (b) sort out the boilerplate code that every
+      <code>monolith</code> application needs. You can find
+      the full program in the source distribution as
+      <code>doc/hello.c</code>.
+    </p>
+
+    <p>
+      Start by including some necessary headers:
+    </p>
+
+<pre>
+#include &lt;pool.h&gt;
+
+#include &lt;monolith.h&gt;
+#include &lt;ml_window.h&gt;
+#include &lt;ml_text_label.h&gt;
+</pre>
+
+    <p>
+      Then there is some standard boilerplate code that needs to
+      go in (once only) into every <code>monolith</code> application.
+      You don't need to worry about what it does: it's just
+      glue between <code>rws</code>'s shared object scripts and
+      the <code>monolith</code> core code:
+    </p>
+
+<pre>
+/*----- The following standard boilerplate code must appear -----*/
+
+/* Main entry point to the app. */
+static void app_main (ml_session);
+
+int
+handle_request (rws_request rq)
+{
+  return ml_entry_point (rq, app_main);
+}
+
+/*----- End of standard boilerplate code -----*/
+</pre>
+
+    <p>
+      Then the "hello, world" program itself:
+    </p>
+
+<pre>
+static void
+app_main (ml_session session)
+{
+  pool pool = ml_session_pool (session);
+  ml_window w;
+  ml_text_label text;
+
+  /* Create the top-level window. */
+  w = new_ml_window (session, pool);
+
+  /* Create the text widget. */
+  text = new_ml_text_label (pool, "Hello, World!");
+
+  /* Pack the text widget into the window. */
+  ml_window_pack (w, text);
+}
+</pre>
+
+    <p>
+      <code>app_main</code> is the entry point into the
+      application. It has one parameter, the opaque
+      <code>ml_session</code> object, which is explained below.  Every
+      <code>monolith</code> program needs a top-level window, so we
+      create this using <code>new_ml_window(3)</code>.
+      Now we need to put something into the window, otherwise
+      it'll appear completely blank. In this case we're
+      going to put a text label widget inside this window
+      containing the familiar greeting. We have to tell the
+      window that it contains the text widget (otherwise
+      they'd be just two completely separate variables),
+      so we call <code>ml_window_pack(3)</code> to place the
+      text label inside the window.
+    </p>
+
+    <p>
+      Now you should compile and run this example
+      (in <code>doc/hello.c</code>) using the instructions
+      above and verify that it runs.
+    </p>
+
+    <h3>Design: sessions, session pools, shared data, session data</h3>
+
+    <p>
+      The <code>app_main</code> function has just one argument
+      passed to it, the opaque <code>ml_session</code> object.
+      What is this used for?
+    </p>
+
+    <p>
+      Think of a normal GUI application written in C or C++ (or
+      another language if you like). A user starts up the
+      application. The application interacts exclusively
+      with that one user. Global variables in the application
+      belong entirely to that application and that user.
+      The application continues to be used until the user
+      closes it down. We will define this process of the user
+      starting up the application, using the application and
+     then finally closing it down, as a <dfn>session</dfn>.
+    </p>
+
+    <p>
+      Web-based applications are slightly different in that
+      the same code running in the same Unix process can be
+      used by many users at the same time. For the benefit
+      of programmers, <code>monolith</code> automatically
+      keeps all of these users' sessions separate for you,
+      maintaining a different opaque <code>ml_session</code> 
+      object for each session.
+    </p>
+
+    <p>
+      One implication of this is that <code>app_main</code> is
+      called with a different <code>ml_session</code> object
+      each time, because a session only starts once. (The same
+      <i>user</i> might come back later and use the same
+      <i>application</i>, but that would be a <em>different session</em>).
+    </p>
+
+    <p>
+      So a session is different from a user. A session is also
+      different from an HTTP request. Typically during a session
+      a user might fill in a few forms, press some buttons,
+      browse through tables and so on. Each of these operations
+      probably involves an HTTP request. So in <code>monolith</code>
+      HTTP requests are very short-lived (of the order of 1ms - 1s),
+      but sessions can be quite long (hours of activity).
+    </p>
+
+    <p>
+      <code>monolith</code> allocates a separate session pool
+      for each session, and most applications are expected
+      to allocate most of their data on this session pool.
+      Of course when a user finishes their session, the session
+      pool will be deleted. This normally results in all of the
+      widgets and stuff created during the session being nuked,
+      and this is generally a good thing.
+    </p>
+
+    <p>
+      C static variables (ie. globals and variables in functions
+      declared using <code>static</code>) are shared between
+      all sessions. This can be useful under some circumstances,
+      but if what you really want is <em>persistent</em> data,
+      then you are far better off using a database of some sort
+      at the back-end. This is because static variables will
+      obviously be trashed if the <code>monolith</code> application
+      is unloaded or the webserver crashes.
+    </p>
+
+    <p>
+      <code>c2lib</code>'s <code>global_pool</code> is also
+      shared between all sessions (but don't use it directly:
+      create a subpool in your <code>_init</code> function
+      and delete the subpool in your <code>_fini</code> function).
+    </p>
+
+    <p>
+      If C static variables are shared, how do we keep a separate
+      set of variables for each session? Generally the easiest
+      way to do this is to allocate a per-session structure
+      once in <code>app_main</code> and pass it around. This
+      is called the <dfn>session data structure</dfn>. We'll
+      see this being done in example 01 below.
+    </p>
+
+    <h3>Design (for CGI programmers): what are widgets?</h3>
+
+    <p>
+      A short aside: what are widgets? If you have any experience
+      of using a traditional GUI library or GUI-builder tool, like
+      Tcl/Tk, gtk, Windows MFC, Motif, Java Swing, etc., then you'll
+      probably already know what a widget (or "control" in MS-speak) is,
+      so skip to the next section. This section is for people
+      coming from a non-graphical or purely CGI background.
+    </p>
+
+    <p>
+      In traditional GUI environments, applications are not
+      built up using pages, forms, CGI scripts and so on,
+      but are instead built up using small reusable objects
+      called widgets. A typical basic widget might be a
+      push button, a label, or an image. There are also
+      compound widgets which store other widgets inside
+      themselves. In <code>monolith</code> for example,
+      a table layout can be used to arrange other widgets
+      into a table. A table layout is itself a widget, and
+      you can use a table layout (populated with widgets inside)
+      any place you would use a basic widget. This is important
+      because complex layouts are often constructed from several
+      layers of compound and basic widgets (buttons and labels inside
+      table layouts inside other table layouts inside windows, etc.)
+    </p>
+
+    <p>
+      Here is an example form constructed using nested widgets:
+    </p>
+
+    <span class="box">
+      <i> Window </i>
+      <span class="box">
+       <i> Form </i>
+       <span class="box">
+         <i> Table layout </i>
+    <table border="1" width="100%">
+       <tr>
+         <td colspan="2"> <i>Label</i> </td>
+       </tr>
+       <tr>
+         <td> <i>Label</i> </td>
+         <td> <i>Form input</i> </td>
+       </tr>
+       <tr>
+         <td> <i>Label</i> </td>
+         <td> <i>Form input</i> </td>
+       </tr>
+       <tr>
+         <td> <i>Empty</i> </td>
+         <td> <i>Form submit</i> </td>
+       </tr>
+    </table>
+  </span>
+  </span>
+  </span>
+
+    <h3>Example 01: Label and button</h3>
+
+    <p>
+      The first example, <code>examples/01_label_and_button.c</code>,
+      is very simple. It displays a label and a button. Clicking
+      on the button increments the number on the label. Try this
+      now. Also try running it from two different browsers and
+      machines. Notice how the label starts counting up from 0
+      independently on each machine (demonstrating that each session
+      is really independent because this demo uses a session data
+      structure).
+    </p>
+
+    <p>
+      As before we begin by including some necessary headers and
+      the same boilerplate code as in our "hello, world" example
+      above. I won't repeat that here, because it's identical.
+      Then we declare our session data structure and a few private
+      functions:
+    </p>
+
+<pre>
+struct data
+{
+  ml_label lb;                 /* Label. */
+  int count;                   /* Count of number of button presses. */
+};
+
+static void increment (ml_session, void *);
+static void update_label (pool pool, ml_label lb, int count);
+</pre>
+
+    <p>
+      We keep (a pointer to) the label widget and the count of
+      button presses in our session data structure. In theory
+      we could keep more here, but in fact it's not necessary. These
+      are the only two variables that we need to make "global"
+      to the session, because these are the only two variables
+      which our callback function will need when it comes to
+      update the label. Our callback function is going to be
+      called when the user clicks on the button, as we'll see
+      in a moment.
+    </p>
+
+    <p>
+      The main entry point to our application is called
+      <code>app_main</code>. It's called at the beginning
+      of the session:
+    </p>
+
+<pre>
+static void
+app_main (ml_session session)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data;
+  ml_window w;
+  ml_flow_layout lay;
+  ml_label lb;
+  ml_button b;
+</pre>
+
+    <p>
+      Notice that we use <code>ml_session_pool(3)</code> to get the
+      current session pool, where we are going to make all of our
+      allocations.
+    </p>
+
+    <p>
+      <code>data</code> will point to our session data structure, but
+      we have to allocate and initialise it first:
+    </p>
+
+<pre>
+  /* Create the private, per-session data area and save it in the
+   * session object.
+   */
+  data = pmalloc (pool, sizeof *data);
+  data-&gt;count = 0;
+</pre>
+
+    <p>
+      Next we create the window, label and button. We're going to
+      pack the label and button into a flow layout which is the
+      simplest sort of compound widget: it just displays the widgets
+      inside itself one after another.
+    </p>
+
+<pre>
+  /* Create the top-level window. */
+  w = new_ml_window (session, pool);
+
+  /* Create the flow layout widget which will be packed into the window. */
+  lay = new_ml_flow_layout (pool);
+
+  /* Create the label and button. */
+  data-&gt;lb = lb = new_ml_label (pool, 0);
+  update_label (pool, data-&gt;lb, 0);
+
+  b = new_ml_button (pool, "Push me!");
+  ml_button_set_callback (b, increment, session, data);
+
+  /* Pack the label and button into the flow layout widget. */
+  ml_flow_layout_pack (lay, lb);
+  ml_flow_layout_pack (lay, b);
+
+  /* Pack the flow layout widget into the window. */
+  ml_window_pack (w, lay);
+}
+</pre>
+
+    <p>
+      Notice the call to <code>ml_button_set_callback</code>. When the
+      button is pressed, the <code>increment</code> function will
+      be called like this: <code>increment (session, data)</code>.
+      (Recall that <code>data</code> is our session data pointer).
+    </p>
+
+    <p>
+      This is the definition of <code>increment</code>:
+    </p>
+
+<pre>
+static void
+increment (ml_session session, void *vp)
+{
+  struct data *data = (struct data *) vp;
+
+  update_label (ml_session_pool (session), data-&gt;lb, ++data-&gt;count);
+}
+</pre>
+
+    <p>
+      It increments <code>data-&gt;count</code> and calls
+      <code>update_label</code> which is the function which
+      actually changes the message on the label.
+    </p>
+
+    <p>
+      <code>update_label</code> is defined as:
+    </p>
+
+<pre>
+static void
+update_label (pool pool, ml_label lb, int count)
+{
+  ml_label_set_text (lb,
+                    psprintf (pool,
+                              "Button pressed: &lt;b&gt;%d&lt;/b&gt;&lt;br&gt;",
+                              count));
+}
+</pre>
+
+    <p>
+      (If you are unfamiliar with the function <code>psprintf</code>
+      then you should read the <code>c2lib</code> documentation).
+    </p>
+
+    <p>
+      That's the end of our first significant <code>monolith</code>
+      application! At around 31 lines of code, it's considerably smaller
+      than the equivalent CGI script.
+    </p>
+
+    <h3>Design: Applications and widgets</h3>
+
+    <p>
+      This section talks about one of the more fundamental
+      design considerations you need to think about when first
+      designing a new <code>monolith</code> application.
+      Namely when to build application code and when to
+      build reusable widgets.
+    </p>
+
+    <p>
+      The label and button example above is a complete
+      <code>monolith</code> application. What happens
+      however if we needed to embed this label and button
+      combo in another program (not a very likely scenario,
+      I'll admit, but let's imagine that you've developed
+      a calendar program or something else quite substantial).
+    </p>
+
+    <p>
+      The answer is to turn your application into a reusable
+      widget. Widgets are often composed of many other
+      more fundamental widgets, and in this case it is possible
+      to turn the label and button combo into a full-blown
+      widget. This widget could be used just like any of the
+      core widgets which <code>monolith</code> provides.
+    </p>
+
+    <p>
+      Turning an application into a widget isn't too hard, depending
+      on the complexity of the application itself, but it's better
+      when designing the application if you first of all work out
+      whether the application as a whole -- or parts of the
+      application -- can be designed as reusable widgets.
+    </p>
+
+    <p>
+      If you were designing a calendar program in <code>monolith</code>
+      then you might decompose the design like this:
+    </p>
+
+    <table border="1">
+       <tr>
+         <th> Component </th>
+         <th> Specification </th>
+         <th> Can be used as a widget? </th>
+       </tr>
+       <tr>
+         <td> Whole application </td>
+         <td> Calendar: A tool for storing and tracking daily
+           events, providing appointments, backed up in a database </td>
+         <td> Yes. By writing the whole calendar as a reusable
+           widget, we can include the calendar widget in an Outlook-style
+           personal information manager (PIM). </td>
+       </tr>
+       <tr>
+         <td> New event form </td>
+         <td> Form which appears when the user adds a new event. </td>
+         <td> No. Quite specific to this calendar, so reuse doesn't
+           make much sense. </td>
+       </tr>
+       <tr>
+         <td> Date selector </td>
+         <td> Form input which allows the user to choose a date
+           and automatically verifies it. </td>
+         <td> Yes. This is applicable on many different forms in
+           other applications. </td>
+       </tr>
+    </table>
+
+    <h3>Example 03: Toy calculators</h3>
+
+    <p>
+      Example 03 will demonstrate how to write a simple reusable
+      widget. In fact the reusable widget that we're going to write
+      is the same as the application in example 02 (not covered
+      here, but supplied with the source in the <code>examples/</code>
+      directory). So if you want you can study the process of
+      converting a whole application into a reusable widget.
+    </p>
+
+    <p>
+      The source code for example 03 is divided into three
+      files:
+    </p>
+
+    <ul>
+      <li> <code>03_many_toy_calculators.c</code> <br>
+       The application. This just instantiates four widgets
+       and displays them.
+      <li> <code>toy_calculator.h</code> <br>
+       The widget's header file. Every widget should have a
+       header file defining the external interface which
+       callers are allowed to use, and defining also the
+       opaque widget type (in this case, <code>toy_calculator</code>).
+      <li> <code>toy_calculator.c</code> <br>
+       The actual code which implements the widget.
+    </ul>
+
+    <p>
+      It's helpful at this point if you run the example. You
+      should see four calculators. Try doing some sums. Notice
+      how each calculator acts completely independently of
+      the others.
+    </p>
+
+    <p>
+      Let's start with the application code. This is very simple. It
+      just creates four toy_calculator objects and populates a
+      table layout widget with them:
+    </p>
+
+<pre>
+static void
+app_main (ml_session session)
+{
+  pool pool = ml_session_pool (session);
+  ml_window w;
+  ml_table_layout tbl;
+  toy_calculator calcs[4];
+
+  /* Create the top-level window. */
+  w = new_ml_window (session, pool);
+
+  /* Create a table layout widget to arrange the calculators. */
+  tbl = new_ml_table_layout (pool, 2, 2);
+
+  /* Create the calculators and pack them into the table layout. */
+  calcs[0] = new_toy_calculator (pool, session);
+  ml_table_layout_pack (tbl, calcs[0], 0, 0);
+  calcs[1] = new_toy_calculator (pool, session);
+  ml_table_layout_pack (tbl, calcs[1], 0, 1);
+  calcs[2] = new_toy_calculator (pool, session);
+  ml_table_layout_pack (tbl, calcs[2], 1, 0);
+  calcs[3] = new_toy_calculator (pool, session);
+  ml_table_layout_pack (tbl, calcs[3], 1, 1);
+
+  /* Pack the table into the window. */
+  ml_window_pack (w, tbl);
+}
+</pre>
+
+    <p>
+      The header file <code>toy_calculator.h</code> defines the
+      interface. It's very simple, because there's only one
+      thing you can do with a <code>toy_calculator</code> at
+      the moment, and that's create one by using the
+      <code>new_toy_calculator</code> function. Notice the
+      <a href="http://www.annexia.org/freeware/c2lib/">cdoc</a>
+      documentation in the comments.
+    </p>
+
+<pre>
+#ifndef TOY_CALCULATOR_H
+#define TOY_CALCULATOR_H
+
+#include &lt;pool.h&gt;
+#include &lt;monolith.h&gt;
+
+struct toy_calculator;
+typedef struct toy_calculator *toy_calculator;
+
+/* Function: new_toy_calculator - toy calculator widget
+ *
+ * @code{new_toy_calculator} creates a new reusable toy calculator
+ * widget.
+ */
+extern toy_calculator new_toy_calculator (pool, ml_session);
+
+#endif /* TOY_CALCULATOR_H */
+</pre>
+
+    <p>
+      The actual implementation of the <code>toy_calculator</code>
+      widget is complicated, so we'll take it step by step here.
+      However remember that if all you want to do is to <em>use</em>
+      a <code>toy_calculator</code>, then you needn't worry about
+      the implementation at all. You only need to look at the
+      header file and use it just like you would any other widget.
+    </p>
+
+    <p>
+      <code>toy_calculator.c</code> begins by including many
+      header files.
+    </p>
+
+<pre>
+#include &lt;string.h&gt;
+
+#include &lt;pool.h&gt;
+#include &lt;pstring.h&gt;
+#include &lt;pthr_cgi.h&gt;
+
+#include &lt;monolith.h&gt;
+#include &lt;ml_window.h&gt;
+#include &lt;ml_table_layout.h&gt;
+#include &lt;ml_text_label.h&gt;
+#include &lt;ml_box.h&gt;
+#include &lt;ml_button.h&gt;
+#include &lt;ml_widget.h&gt;
+
+#include "toy_calculator.h"
+</pre>
+
+    <p>
+      Every widget has an associated structure, which is where it
+      stores all its private data. For callers, the widget structure
+      is opaque (notice how it is defined in the
+      <code>toy_calculator.h</code> header file above). The
+      <code>toy_calculator</code> object we've been passing around
+      above is in fact a pointer, typedef'd to <code>struct
+      toy_calculator *</code>. (This is a common coding convention in
+      <code>c2lib</code> and <code>pthrlib</code>). In the actual
+      implementation, obviously we need to see the private members.
+    </p>
+
+    <p>
+      Moreover, every widget structure <strong>must</strong> begin
+      with a pointer to <code>struct ml_widget_operations</code>
+      because the <code>toy_calculator</code> object "inherits"
+      from the abstract base class <code>ml_widget</code>.
+    </p>
+
+    <p>
+      Here is the definition of <code>struct toy_calculator</code>:
+    </p>
+
+<pre>
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations toy_calculator_ops =
+  {
+    repaint: repaint
+  };
+
+struct toy_calculator
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_text_label disp;          /* The display. */
+  char digits[16];             /* Display digits (only 10+1 used). */
+  double reg;                  /* Hidden register. */
+  int op;                      /* Operation: PLUS, MINUS, TIMES, DIVIDE. */
+  ml_box box;                  /* The top-level box. */
+};
+</pre>
+
+    <p>
+      <code>repaint</code> is going to be the function which
+      actually draws our widget, but we'll see that a little bit
+      later.
+    </p>
+
+    <p>
+      The most important function we need to define is
+      <code>new_toy_calculator</code> which creates new
+      <code>toy_calculator</code> widgets. I won't show this
+      function in full because it's quite long (it needs to
+      create one <code>ml_button</code> object for every
+      button on the calculator, and there are 18 of them in all!).
+      But here's the important outline.
+    </p>
+
+    <p>
+      We start by allocating and initialising a new <code>struct
+      toy_calculator</code>.  A pointer to this is stored in <code>w</code>.
+    </p>
+
+<pre>
+toy_calculator
+new_toy_calculator (pool pool, ml_session session)
+{
+  toy_calculator w;
+  ml_box box;
+  ml_table_layout tbl;
+  ml_button b[18];
+  ml_text_label disp;
+
+  w = pmalloc (pool, sizeof *w);
+  w-&gt;ops = &amp;toy_calculator_ops;
+  w-&gt;pool = pool;
+  strcpy (w-&gt;digits, "0");
+  w-&gt;reg = 0;
+  w-&gt;op = 0;
+</pre>
+
+    <p>
+      Then some code creates the actual widgets contained inside
+      the calculator, the top level being a box widget:
+    </p>
+
+<pre>
+  /* Create the box surrounding the calculator. */
+  box = new_ml_box (pool);
+
+  /* A table layout widget is used to arrange the buttons and the screen.
+   * There are 6 rows, each with 4 columns.
+   */
+  tbl = new_ml_table_layout (pool, 6, 4);
+
+  /* Create the numeric buttons. */
+  b[0] = new_ml_button (pool, "0");
+  ml_button_set_callback (b[0], press_button_0, session, w);
+  ml_button_set_key (b[0], 1);
+         :      :     :
+         :      :     :
+         :      :     :
+</pre>
+
+    <p>
+      Finally we pack everything up and return the widget
+      pointer (<code>w</code>):
+    </p>
+
+<pre>
+         :      :     :
+         :      :     :
+         :      :     :
+  /* Pack the table into the box. */
+  ml_box_pack (box, tbl);
+
+  /* Save the display widget in the widget structure so that the
+   * callback functions can update it.
+   */
+  w-&gt;disp = disp;
+
+  /* Save the box, so we can repaint. */
+  w-&gt;box = box;
+
+  return w;
+}
+</pre>
+
+    <p>
+      When a button is pressed, one of the appropriate callback
+      functions is called. Because there are 18 buttons, there are
+      18 separate callback functions, but we only reproduce a few
+      here. Notice how the <code>toy_calculator</code> pointer
+      is passed as the second argument (the <code>void *</code>),
+      so we need to cast this back before using it:
+    </p>
+
+<pre>
+static void press_button_N (toy_calculator, int);
+
+static void
+press_button_0 (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  press_button_N (w, 0);
+}
+
+         :      :     :
+         :      :     :
+         :      :     :
+
+static void
+press_button_N (toy_calculator w, int n)
+{
+  int len;
+
+  if (strcmp (w-&gt;digits, "0") == 0)
+    w-&gt;digits[0] = '\0';
+
+  len = strlen (w-&gt;digits);
+
+  if ((strchr (w-&gt;digits, '.') &amp;&amp; len &lt; 11) || len &lt; 10)
+    {
+      w-&gt;digits[len] = '0' + n;
+      w-&gt;digits[len+1] = '\0';
+      ml_text_label_set_text (w-&gt;disp, w-&gt;digits);
+    }
+}
+</pre>
+
+    <p>
+      Here's the callback function which runs when the [AC] button
+      is pressed:
+    </p>
+
+<pre>
+static void
+press_button_AC (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  strcpy (w-&gt;digits, "0");
+  w-&gt;reg = 0;
+  ml_text_label_set_text (w-&gt;disp, "0");
+}
+</pre>
+
+    <p>
+      Finally our widget must know how to repaint (redisplay) itself.
+      <code>monolith</code> will call the repaint function at
+      the appropriate moment, and it must generate the HTML corresponding
+      to the widget. In the case of this widget, it's a compound
+      widget built up entirely out of core <code>monolith</code>
+      widgets. The top-level widget inside the calculator is the
+      box (<code>w-&gt;box</code>), so we just call the repaint
+      function for that:
+    </p>
+
+<pre>
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  ml_widget_repaint (w-&gt;box, session, windowid, io);
+}
+</pre>
+
+    <p>
+      That's all. Our reusable toy calculator is now complete.
+    </p>
+
+    <h3>Example 06: Forms</h3>
+
+    <p>
+      Example 06 shows the various input controls available
+      when using forms. The example is straightforward, albeit
+      rather long because of the number of different controls
+      demonstrated. We begin with an unusually long list of
+      includes:
+    </p>
+
+<pre>
+#include &lt;string.h&gt;
+
+#include &lt;pool.h&gt;
+#include &lt;pstring.h&gt;
+#include &lt;pthr_cgi.h&gt;
+
+#include &lt;monolith.h&gt;
+#include &lt;ml_window.h&gt;
+#include &lt;ml_text_label.h&gt;
+#include &lt;ml_button.h&gt;
+#include &lt;ml_table_layout.h&gt;
+#include &lt;ml_flow_layout.h&gt;
+#include &lt;ml_form.h&gt;
+#include &lt;ml_form_submit.h&gt;
+#include &lt;ml_form_text.h&gt;
+#include &lt;ml_form_textarea.h&gt;
+#include &lt;ml_form_password.h&gt;
+#include &lt;ml_form_select.h&gt;
+#include &lt;ml_form_radio_group.h&gt;
+#include &lt;ml_form_radio.h&gt;
+#include &lt;ml_form_checkbox.h&gt;
+#include &lt;ml_form_textarea.h&gt;
+</pre>
+
+    <p>
+      The private session data structure contains pointers to the
+      form input widgets so that our callback functions are able
+      to read their contents:
+    </p>
+
+<pre>
+/* Private per-session data. */
+struct data
+{
+  ml_window win;
+
+  /* The form input fields themselves. */
+  ml_form_text familyname, givenname;
+  ml_form_password password;
+  ml_form_select dd, mm, yyyy; /* Date of birth. */
+  ml_form_radio_group gender;
+  ml_form_radio m, f;          /* Gender. */
+  ml_form_checkbox eating, drinking, sleeping; /* Interests */
+  /*ml_form_file photo;            File upload, not yet implemented. */
+  ml_form_select dept;
+  ml_form_textarea comments;
+};
+</pre>
+
+    <p>
+      <code>app_main</code> allocates the session data structure
+      and calls <code>create_form</code> which actually creates
+      the initial form:
+    </p>
+
+<pre>
+static void
+app_main (ml_session session)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data;
+
+  /* Create the private, per-session data area and save it in the
+   * session object.
+   */
+  data = pmalloc (pool, sizeof *data);
+
+  /* Create the top-level window. */
+  data-&gt;win = new_ml_window (session, pool);
+
+  create_form (session, data);
+}
+</pre>
+
+    <p>
+      <code>create_form</code> is quite a long, but not very
+      complex function. It creates an input control of each
+      type. Notice first the visual structure of the form:
+    </p>
+
+    <span class="box">
+      <i> Window </i>
+      <span class="box">
+       <i> Form </i>
+       <span class="box">
+         <i> Table layout </i>
+    <table border="1" width="100%">
+       <tr>
+         <td> <i>Label</i> </td>
+         <td> <i>Form input</i> </td>
+       </tr>
+       <tr>
+         <td> <i>Label</i> </td>
+         <td> <i>Form input</i> </td>
+       </tr>
+       <tr>
+         <td colspan="2"> <i>etc.</i> </td>
+       </tr>
+       <tr>
+         <td> <i>Empty</i> </td>
+         <td> <i>Form submit</i> </td>
+       </tr>
+    </table>
+  </span>
+  </span>
+  </span>
+
+    <p>
+      I will not reproduce all of the form inputs here:
+    </p>
+
+<pre>
+static void
+create_form (ml_session session, void *vp)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data = (struct data *) vp;
+  ml_form form;
+  ml_table_layout tbl;
+  ml_text_label text;
+  ml_form_submit submit;
+  ml_flow_layout flow;
+  int i;
+
+  /* Create the form. */
+  form = new_ml_form (pool);
+  ml_form_set_callback (form, submit_form, session, data);
+
+  /* Create the table. */
+  tbl = new_ml_table_layout (pool, 10, 2);
+
+  /* Create the contents of the form. */
+  text = new_ml_text_label (pool, "Family name / surname");
+  ml_table_layout_pack (tbl, text, 0, 0);
+  data-&gt;familyname = new_ml_form_text (pool, form);
+  ml_table_layout_pack (tbl, data-&gt;familyname, 0, 1);
+
+         :      :     :
+         :      :     :
+         :      :     :
+
+  /* Submit button. */
+  submit = new_ml_form_submit (pool, form, "Submit");
+  ml_table_layout_pack (tbl, submit, 9, 1);
+
+  /* Pack it all up. */
+  ml_form_pack (form, tbl);
+  ml_window_pack (data-&gt;win, form);
+}
+</pre>
+
+    <p>
+      Notice that we set a callback function on the form:
+    </p>
+
+<pre>
+ml_form_set_callback (form, submit_form, session, data);
+</pre>
+
+    <p>
+      When the form is submitted by the user, <code>submit_form
+       (session, data)</code> will be called. <code>submit_form</code>,
+      reproduced next, can read the value that the user entered
+      into each form field by calling <code>ml_form_input_get_value</code>:
+    </p>
+
+<pre>
+static void
+submit_form (ml_session session, void *vp)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data = (struct data *) vp;
+  ml_text_label text;
+  ml_table_layout tbl;
+  ml_button button;
+  const char *str;
+
+  str = psprintf
+    (pool,
+     "Form submitted.\n"
+     "\n"
+     "Family name: %s\n"
+     "Given name: %s\n"
+     "Password: %s\n"
+     "Date of birth: dd = %d, mm = %d, yyyy = %d\n"
+     "Gender: %s\n"
+     "   M is checked: %d  F is checked: %d\n"
+     "Interests: Eating = %d, Drinking = %d, Sleeping = %d\n"
+     "Dept fields checked: [ %s ]\n"
+     "Comments:\n"
+     "--start--\n"
+     "%s\n"
+     "--end--\n",
+     ml_form_input_get_value (data-&gt;familyname),
+     ml_form_input_get_value (data-&gt;givenname),
+     ml_form_input_get_value (data-&gt;password),
+     1 + ml_form_select_get_selection (data-&gt;dd),
+     1 + ml_form_select_get_selection (data-&gt;mm),
+     1900 + ml_form_select_get_selection (data-&gt;yyyy),
+     ml_form_input_get_value (data-&gt;gender),
+     ml_form_radio_get_checked (data-&gt;m),
+     ml_form_radio_get_checked (data-&gt;f),
+     ml_form_input_get_value (data-&gt;eating) ? 1 : 0,
+     ml_form_input_get_value (data-&gt;drinking) ? 1 : 0,
+     ml_form_input_get_value (data-&gt;sleeping) ? 1 : 0,
+     pjoin (pool,
+           pvitostr (pool, ml_form_select_get_selections (data-&gt;dept)), ", "),
+     ml_form_input_get_value (data-&gt;comments));
+
+  tbl = new_ml_table_layout (pool, 2, 1);
+  text = new_ml_text_label (pool, str);
+  ml_table_layout_pack (tbl, text, 0, 0);
+  button = new_ml_button (pool, "Back to form");
+  ml_button_set_callback (button, create_form, session, data);
+  ml_table_layout_pack (tbl, button, 1, 0);
+
+  ml_window_pack (data-&gt;win, tbl);
+}
+</pre>
+
+    <p>
+      Notice how we have added a button called <q>Back to form</q>
+      which calls <code>create_form</code>.
+    </p>
+
+    <h3>The end of this tutorial</h3>
+
+    <p>
+      That's the end of this tutorial. You should be able to
+      go and write <code>monolith</code> applications and
+      widgets now. Full manual pages are included below.
+    </p>
+
+    <h2>The monolith class hierarchy</h2>
+
+    <p>
+      Of course <code>monolith</code> is written in C, so there are no
+      classes <i>per se</i>, but this is the general class hierarchy of
+      <code>monolith</code> widgets and windows:
+    </p>
+
+<pre>
+ml_window: window or frameset
+
+ml_session: user session
+
+ml_widget
+    |
+    |
+    +--        ml_box: draws a box around another widget
+    |
+    +--        ml_button: simple button
+    |
+    +--        ml_dialog: ask the user a question
+    |
+    +--        ml_flow_layout: layout widgets one after another
+    |
+    +--        ml_form: surrounds a collection of form inputs
+    |
+    +--        ml_form_input
+    |      |
+    |      |
+    |      +-- ml_form_checkbox: checkbox (tickbox)
+    |      |
+    |      +-- ml_form_file: file upload input [not implemented]
+    |      |
+    |      +-- ml_form_password: single line password input
+    |      |
+    |      +-- ml_form_radio_group: group of radio buttons
+    |      |
+    |       +--        ml_form_radio: radio button
+    |       |
+    |      +-- ml_form_select: drop-down list of options
+    |      |
+    |      +-- ml_form_submit: submit button
+    |      |
+    |      +-- ml_form_text: single line text input
+    |      |
+    |      +-- ml_form_textarea: multi-line text input
+    |
+    +-- ml_image: display an icon or image
+    |
+    +-- ml_label: display arbitrary HTML
+    |
+    +-- ml_table_layout: powerful table layouts of widgets
+    |
+    +-- ml_text_label: single line or paragraphs of plain text
+    |
+    +--        ml_toggle_button: toggle button
+</pre>
+
+    <p>
+      Inheritance is faked using a technique very similar to the vtables
+      used by C++.
+    </p>
+
+    <h2>Other Notes</h2>
+
+    <h3>Can I theme monolith applications?</h3>
+
+    <p>
+      Yes, you can. At the moment, the easiest way to change the look
+      and feel is to edit the default stylesheet
+      (<code>default.css</code>). This way you can make extensive
+      changes to how your application looks from a single file.
+    </p>
+
+    <p>
+      If you don't like the idea of editing <code>default.css</code>,
+      then copy it and make your own. Call
+      <code>ml_window_set_stylesheet</code> on all your application
+      windows to point to your new stylesheet.
+    </p>
+
+    <p>
+      You can even provide different themes to different users
+      (so-called "skinning" - ugh I hate that term). Once a user has
+      logged into the app, call <code>ml_window_set_stylesheet</code>
+      with the appropriate theme for that user.
+    </p>
+
+    <h3>Frames considered harmful</h3>
+
+    <p>
+      Although <code>monolith</code> supports frames and pop-up
+      windows, care should be taken because these do not work in
+      the way that most users expect.
+    </p>
+
+    <p>
+      The problem arises when one frame tries to update the state
+      of another frame (the same problem applies to two separate
+      windows, but I'll just use the generic term "frame" here).
+      For example, imagine the following simple frameset:
+    </p>
+
+    <table border="1">
+       <tr>
+         <td style="width: 100px">
+           <p><i>Left frame</i></p>
+           <p>[Button 1]</p>
+           <p>[Button 2]</p>
+           <p>[Button 3]</p>
+         </td>
+         <td style="width: 200px" valign="top">
+           <p><i>Right frame</i></p>
+         </td>
+       </tr>
+    </table>
+
+    <p>
+      It's common to want the buttons in the left hand frame to
+      change the contents displayed in the right hand frame, and
+      a naive way to do this would be to set the callback for
+      each button to a function like this:
+    </p>
+
+<pre>
+static void
+update_right_frame (ml_session session, void *vp)
+{
+  /* Get private per-session data. */
+  struct data *data = (struct data *) vp;
+
+  pool pool = ml_session_pool (session);
+
+  ml_text_label label = new_ml_text_label (pool, <i>updated content</i>);
+
+  /* Change the contents of the right hand frame. */
+  ml_window_pack (data-&gt;right_frame, label);
+}
+</pre>
+
+    <p>
+      The problem is that this doesn't work at all. Pressing the
+      buttons will not update the right hand frame.
+    </p>
+
+    <p>
+      For seasoned HTML programmers, it will be obvious why this
+      happens. For people used to traditional application development,
+      it is confusing and seems like a bug.
+    </p>
+
+    <p>
+      In future, we will add features to Monolith to allow careful
+      developers to use frames in situations such as above. However
+      at the moment, the advice is:
+    </p>
+
+    <ol>
+      <li> Consider each frame/window/pop-up to be a completely
+       separate entity, almost like a separate session. Don't
+       expect that updating properties in another frame/window/pop-up
+       will work (it almost certainly will have no effect).
+      <li> Avoid using framesets if possible. Table layouts are
+       often a better substitute.
+    </ol>
+
+    <h3>Notes on forms</h3>
+
+    <p>
+      You cannot nest forms. (This is a limitation of HTML.)
+    </p>
+
+    <p>
+      If you have several forms on the same page, you can
+      run into problems. A common problem happens when you
+      have two forms on the same dialog like this:
+    </p>
+
+    <table border="1">
+       <tr>
+         <td>
+           <i>First form</i><br>
+           [ --- input #1 --- ] [submit]
+         </td>
+       </tr>
+       <tr>
+         <td>
+           <i>Second form</i><br>
+           [ --- input #2 --- ] [submit]
+         </td>
+       </tr>
+    </table>
+
+    <p>
+      If the user types something in input field #1, then types
+      something in input field #2, and presses the second submit
+      button, then the contents of input field #1 will disappear.
+    </p>
+
+    <p>
+      To avoid this, either only use one form on a dialog, or design
+      your dialogs so that it is clear that the first submit button
+      must be pressed after filling out the first form.
+    </p>
+
+    <p>
+      Another problem with forms (and again, a limitation of HTML)
+      is that they are not interactive. The server cannot read the
+      value of a form input field until the [Submit] button has
+      been pressed.
+    </p>
+
+    <p>
+      If you are using forms, then only use form input widgets
+      inside them. Toggle buttons and so on are not form input
+      widgets, and cannot be part of a form.
+    </p>
+
+    <h2>Links to manual pages</h2>
+
+    <ul>
+      <li> <a href="ml_box_pack.3.html"><code>ml_box_pack(3)</code></a> </li>
+      <li> <a href="ml_button_get_text.3.html"><code>ml_button_get_text(3)</code></a> </li>
+      <li> <a href="ml_button_set_callback.3.html"><code>ml_button_set_callback(3)</code></a> </li>
+      <li> <a href="ml_button_set_key.3.html"><code>ml_button_set_key(3)</code></a> </li>
+      <li> <a href="ml_button_set_text.3.html"><code>ml_button_set_text(3)</code></a> </li>
+      <li> <a href="ml_dialog_add_button.3.html"><code>ml_dialog_add_button(3)</code></a> </li>
+      <li> <a href="ml_dialog_clear_buttons.3.html"><code>ml_dialog_clear_buttons(3)</code></a> </li>
+      <li> <a href="ml_dialog_get_icon.3.html"><code>ml_dialog_get_icon(3)</code></a> </li>
+      <li> <a href="ml_dialog_get_text.3.html"><code>ml_dialog_get_text(3)</code></a> </li>
+      <li> <a href="ml_dialog_get_title.3.html"><code>ml_dialog_get_title(3)</code></a> </li>
+      <li> <a href="ml_dialog_set_icon.3.html"><code>ml_dialog_set_icon(3)</code></a> </li>
+      <li> <a href="ml_dialog_set_text.3.html"><code>ml_dialog_set_text(3)</code></a> </li>
+      <li> <a href="ml_dialog_set_title.3.html"><code>ml_dialog_set_title(3)</code></a> </li>
+      <li> <a href="ml_entry_point.3.html"><code>ml_entry_point(3)</code></a> </li>
+      <li> <a href="ml_flow_layout_clear.3.html"><code>ml_flow_layout_clear(3)</code></a> </li>
+      <li> <a href="ml_flow_layout_erase.3.html"><code>ml_flow_layout_erase(3)</code></a> </li>
+      <li> <a href="ml_flow_layout_get.3.html"><code>ml_flow_layout_get(3)</code></a> </li>
+      <li> <a href="ml_flow_layout_insert.3.html"><code>ml_flow_layout_insert(3)</code></a> </li>
+      <li> <a href="ml_flow_layout_pack.3.html"><code>ml_flow_layout_pack(3)</code></a> </li>
+      <li> <a href="ml_flow_layout_pop_back.3.html"><code>ml_flow_layout_pop_back(3)</code></a> </li>
+      <li> <a href="ml_flow_layout_pop_front.3.html"><code>ml_flow_layout_pop_front(3)</code></a> </li>
+      <li> <a href="ml_flow_layout_push_back.3.html"><code>ml_flow_layout_push_back(3)</code></a> </li>
+      <li> <a href="ml_flow_layout_push_front.3.html"><code>ml_flow_layout_push_front(3)</code></a> </li>
+      <li> <a href="ml_flow_layout_replace.3.html"><code>ml_flow_layout_replace(3)</code></a> </li>
+      <li> <a href="ml_flow_layout_size.3.html"><code>ml_flow_layout_size(3)</code></a> </li>
+      <li> <a href="ml_form_input_clear_value.3.html"><code>ml_form_input_clear_value(3)</code></a> </li>
+      <li> <a href="ml_form_input_get_value.3.html"><code>ml_form_input_get_value(3)</code></a> </li>
+      <li> <a href="ml_form_input_set_value.3.html"><code>ml_form_input_set_value(3)</code></a> </li>
+      <li> <a href="ml_form_radio_get_checked.3.html"><code>ml_form_radio_get_checked(3)</code></a> </li>
+      <li> <a href="ml_form_radio_group_pack.3.html"><code>ml_form_radio_group_pack(3)</code></a> </li>
+      <li> <a href="ml_form_radio_set_checked.3.html"><code>ml_form_radio_set_checked(3)</code></a> </li>
+      <li> <a href="ml_form_select_clear.3.html"><code>ml_form_select_clear(3)</code></a> </li>
+      <li> <a href="ml_form_select_erase.3.html"><code>ml_form_select_erase(3)</code></a> </li>
+      <li> <a href="ml_form_select_get.3.html"><code>ml_form_select_get(3)</code></a> </li>
+      <li> <a href="ml_form_select_get_multiple.3.html"><code>ml_form_select_get_multiple(3)</code></a> </li>
+      <li> <a href="ml_form_select_get_selection.3.html"><code>ml_form_select_get_selection(3)</code></a> </li>
+      <li> <a href="ml_form_select_get_selections.3.html"><code>ml_form_select_get_selections(3)</code></a> </li>
+      <li> <a href="ml_form_select_get_size.3.html"><code>ml_form_select_get_size(3)</code></a> </li>
+      <li> <a href="ml_form_select_insert.3.html"><code>ml_form_select_insert(3)</code></a> </li>
+      <li> <a href="ml_form_select_pop_back.3.html"><code>ml_form_select_pop_back(3)</code></a> </li>
+      <li> <a href="ml_form_select_pop_front.3.html"><code>ml_form_select_pop_front(3)</code></a> </li>
+      <li> <a href="ml_form_select_push_back.3.html"><code>ml_form_select_push_back(3)</code></a> </li>
+      <li> <a href="ml_form_select_push_front.3.html"><code>ml_form_select_push_front(3)</code></a> </li>
+      <li> <a href="ml_form_select_replace.3.html"><code>ml_form_select_replace(3)</code></a> </li>
+      <li> <a href="ml_form_select_set_multiple.3.html"><code>ml_form_select_set_multiple(3)</code></a> </li>
+      <li> <a href="ml_form_select_set_selection.3.html"><code>ml_form_select_set_selection(3)</code></a> </li>
+      <li> <a href="ml_form_select_set_selections.3.html"><code>ml_form_select_set_selections(3)</code></a> </li>
+      <li> <a href="ml_form_select_set_size.3.html"><code>ml_form_select_set_size(3)</code></a> </li>
+      <li> <a href="ml_form_select_size.3.html"><code>ml_form_select_size(3)</code></a> </li>
+      <li> <a href="ml_frameset_get_title.3.html"><code>ml_frameset_get_title(3)</code></a> </li>
+      <li> <a href="ml_frameset_set_description.3.html"><code>ml_frameset_set_description(3)</code></a> </li>
+      <li> <a href="ml_frameset_set_title.3.html"><code>ml_frameset_set_title(3)</code></a> </li>
+      <li> <a href="ml_image_get_src.3.html"><code>ml_image_get_src(3)</code></a> </li>
+      <li> <a href="ml_image_set_src.3.html"><code>ml_image_set_src(3)</code></a> </li>
+      <li> <a href="ml_label_get_text.3.html"><code>ml_label_get_text(3)</code></a> </li>
+      <li> <a href="ml_label_set_text.3.html"><code>ml_label_set_text(3)</code></a> </li>
+      <li> <a href="ml_register_action.3.html"><code>ml_register_action(3)</code></a> </li>
+      <li> <a href="ml_session_args.3.html"><code>ml_session_args(3)</code></a> </li>
+      <li> <a href="ml_session_canonical_path.3.html"><code>ml_session_canonical_path(3)</code></a> </li>
+      <li> <a href="ml_session_pool.3.html"><code>ml_session_pool(3)</code></a> </li>
+      <li> <a href="ml_session_script_name.3.html"><code>ml_session_script_name(3)</code></a> </li>
+      <li> <a href="ml_session_sessionid.3.html"><code>ml_session_sessionid(3)</code></a> </li>
+      <li> <a href="ml_table_layout_pack.3.html"><code>ml_table_layout_pack(3)</code></a> </li>
+      <li> <a href="ml_table_layout_set_align.3.html"><code>ml_table_layout_set_align(3)</code></a> </li>
+      <li> <a href="ml_table_layout_set_colspan.3.html"><code>ml_table_layout_set_colspan(3)</code></a> </li>
+      <li> <a href="ml_table_layout_set_rowspan.3.html"><code>ml_table_layout_set_rowspan(3)</code></a> </li>
+      <li> <a href="ml_table_layout_set_valign.3.html"><code>ml_table_layout_set_valign(3)</code></a> </li>
+      <li> <a href="ml_text_label_set_font_size.3.html"><code>ml_text_label_set_font_size(3)</code></a> </li>
+      <li> <a href="ml_text_label_set_font_weight.3.html"><code>ml_text_label_set_font_weight(3)</code></a> </li>
+      <li> <a href="ml_text_label_set_text.3.html"><code>ml_text_label_set_text(3)</code></a> </li>
+      <li> <a href="ml_text_label_set_text_align.3.html"><code>ml_text_label_set_text_align(3)</code></a> </li>
+      <li> <a href="ml_unregister_action.3.html"><code>ml_unregister_action(3)</code></a> </li>
+      <li> <a href="ml_widget_repaint.3.html"><code>ml_widget_repaint(3)</code></a> </li>
+      <li> <a href="ml_window_get_charset.3.html"><code>ml_window_get_charset(3)</code></a> </li>
+      <li> <a href="ml_window_get_stylesheet.3.html"><code>ml_window_get_stylesheet(3)</code></a> </li>
+      <li> <a href="ml_window_get_title.3.html"><code>ml_window_get_title(3)</code></a> </li>
+      <li> <a href="ml_window_pack.3.html"><code>ml_window_pack(3)</code></a> </li>
+      <li> <a href="ml_window_set_charset.3.html"><code>ml_window_set_charset(3)</code></a> </li>
+      <li> <a href="ml_window_set_stylesheet.3.html"><code>ml_window_set_stylesheet(3)</code></a> </li>
+      <li> <a href="ml_window_set_title.3.html"><code>ml_window_set_title(3)</code></a> </li>
+      <li> <a href="new_ml_box.3.html"><code>new_ml_box(3)</code></a> </li>
+      <li> <a href="new_ml_button.3.html"><code>new_ml_button(3)</code></a> </li>
+      <li> <a href="new_ml_dialog.3.html"><code>new_ml_dialog(3)</code></a> </li>
+      <li> <a href="new_ml_flow_layout.3.html"><code>new_ml_flow_layout(3)</code></a> </li>
+      <li> <a href="new_ml_form.3.html"><code>new_ml_form(3)</code></a> </li>
+      <li> <a href="new_ml_form_checkbox.3.html"><code>new_ml_form_checkbox(3)</code></a> </li>
+      <li> <a href="new_ml_form_password.3.html"><code>new_ml_form_password(3)</code></a> </li>
+      <li> <a href="new_ml_form_radio.3.html"><code>new_ml_form_radio(3)</code></a> </li>
+      <li> <a href="new_ml_form_radio_group.3.html"><code>new_ml_form_radio_group(3)</code></a> </li>
+      <li> <a href="new_ml_form_select.3.html"><code>new_ml_form_select(3)</code></a> </li>
+      <li> <a href="new_ml_form_submit.3.html"><code>new_ml_form_submit(3)</code></a> </li>
+      <li> <a href="new_ml_form_text.3.html"><code>new_ml_form_text(3)</code></a> </li>
+      <li> <a href="new_ml_form_textarea.3.html"><code>new_ml_form_textarea(3)</code></a> </li>
+      <li> <a href="new_ml_frameset.3.html"><code>new_ml_frameset(3)</code></a> </li>
+      <li> <a href="new_ml_image.3.html"><code>new_ml_image(3)</code></a> </li>
+      <li> <a href="new_ml_label.3.html"><code>new_ml_label(3)</code></a> </li>
+      <li> <a href="new_ml_table_layout.3.html"><code>new_ml_table_layout(3)</code></a> </li>
+      <li> <a href="new_ml_text_label.3.html"><code>new_ml_text_label(3)</code></a> </li>
+      <li> <a href="new_ml_window.3.html"><code>new_ml_window(3)</code></a> </li>
+    </ul>
+
+    <hr>
+    <address><a href="mailto:rich@annexia.org">Richard Jones</a></address>
+<!-- Created: Wed May  1 19:36:16 BST 2002 -->
+<!-- hhmts start -->
+Last modified: Sat Sep  7 14:45:50 BST 2002
+<!-- hhmts end -->
+  </body>
+</html>
diff --git a/doc/ml_box_pack.3.html b/doc/ml_box_pack.3.html
new file mode 100644 (file)
index 0000000..df3a313
--- /dev/null
@@ -0,0 +1,89 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_box</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_box</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:50 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_box, ml_box_pack - monolith box widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_box.h&gt;
+
+ml_box new_ml_box (pool pool);
+void ml_box_pack (ml_box, ml_widget);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The monolith box widget encloses another widget inside a
+rectangular outline.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_box</b> creates a new box widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_box_pack</b> packs another widget inside the box. A
+maximum of one widget can be packed inside a box, so if you
+call this function multiple times, then earlier widgets will
+be forgotten.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_button_get_text.3.html b/doc/ml_button_get_text.3.html
new file mode 100644 (file)
index 0000000..1b84185
--- /dev/null
@@ -0,0 +1,147 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_button</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_button</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:50 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_button, ml_button_set_text, ml_button_get_text, ml_button_set_callback, ml_button_set_key - monolith button widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_button.h&gt;
+
+ml_button new_ml_button (pool pool, const char *text);
+void ml_button_set_text (ml_button, const char *text);
+const char *ml_button_get_text (ml_button);
+void ml_button_set_callback (ml_button, void (*fn)(ml_session, void *), ml_session, void *);
+void ml_button_set_key (ml_button, int is_key);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A button widget is a simple button which calls a function
+when clicked.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_button</b> creates a new button
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_(set|get)_text</b> updates the HTML text
+printed on the button. Note that the text string must be
+either static, or already allocated in the button's pool, or
+allocated in a pool with a longer lifetime than the button.
+If the text is set to <b>NULL</b> then this has the same
+effect as setting the text to the empty string, which is not
+very useful because the button can never be
+pressed.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_callback</b> updates the callback function
+which is invoked when the button is pressed.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_key</b> sets the boolean &quot;key&quot;
+property of the button. This should be set on buttons where
+the text is a single letter, suitable for, say, calculator
+keys. All key buttons are rendered at a fixed width of
+&quot;1em&quot;, resulting in a more pleasing overall
+effect.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_popup</b> sets the popup property of the
+button. When the user clicks on a popup button, a new popup
+window opens on the screen. To make a button into a popup
+button, the popup property should be a non-<b>NULL</b>
+string which is the name of the new browser window (note
+that because of limitations in HTML, browser window names
+are global, so it is recommended that names be
+&quot;<b>appname</b>_<b>name</b>&quot;). To change a popup
+button back to an ordinary button, set the popup property to
+<b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The callback used by a popup button must create a top-level
+window, otherwise you will get an internal server
+error.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_popup_size</b> changes the size of the
+popup window. The default is width 0, height 0, which
+usually creates a popup window which is the same size and
+shape as the current browser window.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_button_set_callback.3.html b/doc/ml_button_set_callback.3.html
new file mode 100644 (file)
index 0000000..1a40931
--- /dev/null
@@ -0,0 +1,147 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_button</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_button</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:51 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_button, ml_button_set_text, ml_button_get_text, ml_button_set_callback, ml_button_set_key - monolith button widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_button.h&gt;
+
+ml_button new_ml_button (pool pool, const char *text);
+void ml_button_set_text (ml_button, const char *text);
+const char *ml_button_get_text (ml_button);
+void ml_button_set_callback (ml_button, void (*fn)(ml_session, void *), ml_session, void *);
+void ml_button_set_key (ml_button, int is_key);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A button widget is a simple button which calls a function
+when clicked.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_button</b> creates a new button
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_(set|get)_text</b> updates the HTML text
+printed on the button. Note that the text string must be
+either static, or already allocated in the button's pool, or
+allocated in a pool with a longer lifetime than the button.
+If the text is set to <b>NULL</b> then this has the same
+effect as setting the text to the empty string, which is not
+very useful because the button can never be
+pressed.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_callback</b> updates the callback function
+which is invoked when the button is pressed.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_key</b> sets the boolean &quot;key&quot;
+property of the button. This should be set on buttons where
+the text is a single letter, suitable for, say, calculator
+keys. All key buttons are rendered at a fixed width of
+&quot;1em&quot;, resulting in a more pleasing overall
+effect.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_popup</b> sets the popup property of the
+button. When the user clicks on a popup button, a new popup
+window opens on the screen. To make a button into a popup
+button, the popup property should be a non-<b>NULL</b>
+string which is the name of the new browser window (note
+that because of limitations in HTML, browser window names
+are global, so it is recommended that names be
+&quot;<b>appname</b>_<b>name</b>&quot;). To change a popup
+button back to an ordinary button, set the popup property to
+<b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The callback used by a popup button must create a top-level
+window, otherwise you will get an internal server
+error.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_popup_size</b> changes the size of the
+popup window. The default is width 0, height 0, which
+usually creates a popup window which is the same size and
+shape as the current browser window.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_button_set_key.3.html b/doc/ml_button_set_key.3.html
new file mode 100644 (file)
index 0000000..1a40931
--- /dev/null
@@ -0,0 +1,147 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_button</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_button</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:51 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_button, ml_button_set_text, ml_button_get_text, ml_button_set_callback, ml_button_set_key - monolith button widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_button.h&gt;
+
+ml_button new_ml_button (pool pool, const char *text);
+void ml_button_set_text (ml_button, const char *text);
+const char *ml_button_get_text (ml_button);
+void ml_button_set_callback (ml_button, void (*fn)(ml_session, void *), ml_session, void *);
+void ml_button_set_key (ml_button, int is_key);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A button widget is a simple button which calls a function
+when clicked.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_button</b> creates a new button
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_(set|get)_text</b> updates the HTML text
+printed on the button. Note that the text string must be
+either static, or already allocated in the button's pool, or
+allocated in a pool with a longer lifetime than the button.
+If the text is set to <b>NULL</b> then this has the same
+effect as setting the text to the empty string, which is not
+very useful because the button can never be
+pressed.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_callback</b> updates the callback function
+which is invoked when the button is pressed.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_key</b> sets the boolean &quot;key&quot;
+property of the button. This should be set on buttons where
+the text is a single letter, suitable for, say, calculator
+keys. All key buttons are rendered at a fixed width of
+&quot;1em&quot;, resulting in a more pleasing overall
+effect.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_popup</b> sets the popup property of the
+button. When the user clicks on a popup button, a new popup
+window opens on the screen. To make a button into a popup
+button, the popup property should be a non-<b>NULL</b>
+string which is the name of the new browser window (note
+that because of limitations in HTML, browser window names
+are global, so it is recommended that names be
+&quot;<b>appname</b>_<b>name</b>&quot;). To change a popup
+button back to an ordinary button, set the popup property to
+<b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The callback used by a popup button must create a top-level
+window, otherwise you will get an internal server
+error.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_popup_size</b> changes the size of the
+popup window. The default is width 0, height 0, which
+usually creates a popup window which is the same size and
+shape as the current browser window.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_button_set_text.3.html b/doc/ml_button_set_text.3.html
new file mode 100644 (file)
index 0000000..1a40931
--- /dev/null
@@ -0,0 +1,147 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_button</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_button</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:51 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_button, ml_button_set_text, ml_button_get_text, ml_button_set_callback, ml_button_set_key - monolith button widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_button.h&gt;
+
+ml_button new_ml_button (pool pool, const char *text);
+void ml_button_set_text (ml_button, const char *text);
+const char *ml_button_get_text (ml_button);
+void ml_button_set_callback (ml_button, void (*fn)(ml_session, void *), ml_session, void *);
+void ml_button_set_key (ml_button, int is_key);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A button widget is a simple button which calls a function
+when clicked.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_button</b> creates a new button
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_(set|get)_text</b> updates the HTML text
+printed on the button. Note that the text string must be
+either static, or already allocated in the button's pool, or
+allocated in a pool with a longer lifetime than the button.
+If the text is set to <b>NULL</b> then this has the same
+effect as setting the text to the empty string, which is not
+very useful because the button can never be
+pressed.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_callback</b> updates the callback function
+which is invoked when the button is pressed.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_key</b> sets the boolean &quot;key&quot;
+property of the button. This should be set on buttons where
+the text is a single letter, suitable for, say, calculator
+keys. All key buttons are rendered at a fixed width of
+&quot;1em&quot;, resulting in a more pleasing overall
+effect.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_popup</b> sets the popup property of the
+button. When the user clicks on a popup button, a new popup
+window opens on the screen. To make a button into a popup
+button, the popup property should be a non-<b>NULL</b>
+string which is the name of the new browser window (note
+that because of limitations in HTML, browser window names
+are global, so it is recommended that names be
+&quot;<b>appname</b>_<b>name</b>&quot;). To change a popup
+button back to an ordinary button, set the popup property to
+<b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The callback used by a popup button must create a top-level
+window, otherwise you will get an internal server
+error.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_popup_size</b> changes the size of the
+popup window. The default is width 0, height 0, which
+usually creates a popup window which is the same size and
+shape as the current browser window.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_dialog_add_button.3.html b/doc/ml_dialog_add_button.3.html
new file mode 100644 (file)
index 0000000..d4c6dde
--- /dev/null
@@ -0,0 +1,161 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_dialog</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_dialog</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:51 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_dialog, ml_dialog_set_text, ml_dialog_get_text, ml_dialog_set_title, ml_dialog_get_title, ml_dialog_set_icon, ml_dialog_get_icon, ml_dialog_clear_buttons, ml_dialog_add_button - monolith dialog widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_dialog.h&gt;
+
+ml_dialog new_ml_dialog (pool pool);
+void ml_dialog_set_text (ml_dialog, const char *text);
+const char *ml_dialog_get_text (ml_dialog);
+void ml_dialog_set_title (ml_dialog, const char *title);
+const char *ml_dialog_get_title (ml_dialog);
+void ml_dialog_set_icon (ml_dialog, const char *icon);
+const char *ml_dialog_get_icon (ml_dialog);
+void ml_dialog_clear_buttons (ml_dialog);
+void ml_dialog_add_button (ml_dialog, const char *text, void (*callback_fn) (ml_session, void *), ml_session session, void *data);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A dialog is a widget for asking a user a question, and
+getting an answer. It can also be used for presenting the
+user with confirmation that some operation has been carried
+out, or for presenting the user with an error
+message.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Fundamentally, a dialog consists of some text, and a series
+of zero or more buttons along the bottom of the widget. A
+dialog can also have an optional title which appears along
+the top and an optional icon which appears on the left of
+the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Dialogs are almost always used packed directly into a
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Dialogs are actually &quot;super-widgets&quot;, built up
+from fundamental widgets like labels, buttons, and table
+layouts. However currently none of the fundamental widgets
+are actually exposed through the <b>ml_dialog_*</b>
+interface.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_dialog</b> creates a new dialog widget with no
+text, no title, no icon and no buttons (ie. not very useful!
+- after calling this you should immediately call
+<b>ml_dialog_set_text</b> and probably
+<b>ml_dialog_add_button</b> too).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_text</b> updates the text in a dialog
+widget. Although having text is not strictly mandatory, it
+is highly advisable.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_title</b> changes the title. To have
+no title, set the title to <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_icon</b> changes the icon. To have no
+icon, set the icon to <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_clear_buttons</b> removes all the buttons from
+the dialog.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_add_button</b> adds a single button to the
+dialog. If there are already buttons attached to the dialog,
+then this adds the new button on the right. The <b>text</b>
+which appears on the button must be specified. When the
+button is pressed, <b>callback_fn</b> will be
+called.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_dialog_clear_buttons.3.html b/doc/ml_dialog_clear_buttons.3.html
new file mode 100644 (file)
index 0000000..f5ffbf8
--- /dev/null
@@ -0,0 +1,161 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_dialog</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_dialog</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:52 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_dialog, ml_dialog_set_text, ml_dialog_get_text, ml_dialog_set_title, ml_dialog_get_title, ml_dialog_set_icon, ml_dialog_get_icon, ml_dialog_clear_buttons, ml_dialog_add_button - monolith dialog widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_dialog.h&gt;
+
+ml_dialog new_ml_dialog (pool pool);
+void ml_dialog_set_text (ml_dialog, const char *text);
+const char *ml_dialog_get_text (ml_dialog);
+void ml_dialog_set_title (ml_dialog, const char *title);
+const char *ml_dialog_get_title (ml_dialog);
+void ml_dialog_set_icon (ml_dialog, const char *icon);
+const char *ml_dialog_get_icon (ml_dialog);
+void ml_dialog_clear_buttons (ml_dialog);
+void ml_dialog_add_button (ml_dialog, const char *text, void (*callback_fn) (ml_session, void *), ml_session session, void *data);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A dialog is a widget for asking a user a question, and
+getting an answer. It can also be used for presenting the
+user with confirmation that some operation has been carried
+out, or for presenting the user with an error
+message.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Fundamentally, a dialog consists of some text, and a series
+of zero or more buttons along the bottom of the widget. A
+dialog can also have an optional title which appears along
+the top and an optional icon which appears on the left of
+the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Dialogs are almost always used packed directly into a
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Dialogs are actually &quot;super-widgets&quot;, built up
+from fundamental widgets like labels, buttons, and table
+layouts. However currently none of the fundamental widgets
+are actually exposed through the <b>ml_dialog_*</b>
+interface.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_dialog</b> creates a new dialog widget with no
+text, no title, no icon and no buttons (ie. not very useful!
+- after calling this you should immediately call
+<b>ml_dialog_set_text</b> and probably
+<b>ml_dialog_add_button</b> too).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_text</b> updates the text in a dialog
+widget. Although having text is not strictly mandatory, it
+is highly advisable.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_title</b> changes the title. To have
+no title, set the title to <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_icon</b> changes the icon. To have no
+icon, set the icon to <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_clear_buttons</b> removes all the buttons from
+the dialog.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_add_button</b> adds a single button to the
+dialog. If there are already buttons attached to the dialog,
+then this adds the new button on the right. The <b>text</b>
+which appears on the button must be specified. When the
+button is pressed, <b>callback_fn</b> will be
+called.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_dialog_get_icon.3.html b/doc/ml_dialog_get_icon.3.html
new file mode 100644 (file)
index 0000000..f5ffbf8
--- /dev/null
@@ -0,0 +1,161 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_dialog</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_dialog</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:52 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_dialog, ml_dialog_set_text, ml_dialog_get_text, ml_dialog_set_title, ml_dialog_get_title, ml_dialog_set_icon, ml_dialog_get_icon, ml_dialog_clear_buttons, ml_dialog_add_button - monolith dialog widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_dialog.h&gt;
+
+ml_dialog new_ml_dialog (pool pool);
+void ml_dialog_set_text (ml_dialog, const char *text);
+const char *ml_dialog_get_text (ml_dialog);
+void ml_dialog_set_title (ml_dialog, const char *title);
+const char *ml_dialog_get_title (ml_dialog);
+void ml_dialog_set_icon (ml_dialog, const char *icon);
+const char *ml_dialog_get_icon (ml_dialog);
+void ml_dialog_clear_buttons (ml_dialog);
+void ml_dialog_add_button (ml_dialog, const char *text, void (*callback_fn) (ml_session, void *), ml_session session, void *data);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A dialog is a widget for asking a user a question, and
+getting an answer. It can also be used for presenting the
+user with confirmation that some operation has been carried
+out, or for presenting the user with an error
+message.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Fundamentally, a dialog consists of some text, and a series
+of zero or more buttons along the bottom of the widget. A
+dialog can also have an optional title which appears along
+the top and an optional icon which appears on the left of
+the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Dialogs are almost always used packed directly into a
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Dialogs are actually &quot;super-widgets&quot;, built up
+from fundamental widgets like labels, buttons, and table
+layouts. However currently none of the fundamental widgets
+are actually exposed through the <b>ml_dialog_*</b>
+interface.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_dialog</b> creates a new dialog widget with no
+text, no title, no icon and no buttons (ie. not very useful!
+- after calling this you should immediately call
+<b>ml_dialog_set_text</b> and probably
+<b>ml_dialog_add_button</b> too).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_text</b> updates the text in a dialog
+widget. Although having text is not strictly mandatory, it
+is highly advisable.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_title</b> changes the title. To have
+no title, set the title to <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_icon</b> changes the icon. To have no
+icon, set the icon to <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_clear_buttons</b> removes all the buttons from
+the dialog.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_add_button</b> adds a single button to the
+dialog. If there are already buttons attached to the dialog,
+then this adds the new button on the right. The <b>text</b>
+which appears on the button must be specified. When the
+button is pressed, <b>callback_fn</b> will be
+called.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_dialog_get_text.3.html b/doc/ml_dialog_get_text.3.html
new file mode 100644 (file)
index 0000000..f5ffbf8
--- /dev/null
@@ -0,0 +1,161 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_dialog</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_dialog</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:52 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_dialog, ml_dialog_set_text, ml_dialog_get_text, ml_dialog_set_title, ml_dialog_get_title, ml_dialog_set_icon, ml_dialog_get_icon, ml_dialog_clear_buttons, ml_dialog_add_button - monolith dialog widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_dialog.h&gt;
+
+ml_dialog new_ml_dialog (pool pool);
+void ml_dialog_set_text (ml_dialog, const char *text);
+const char *ml_dialog_get_text (ml_dialog);
+void ml_dialog_set_title (ml_dialog, const char *title);
+const char *ml_dialog_get_title (ml_dialog);
+void ml_dialog_set_icon (ml_dialog, const char *icon);
+const char *ml_dialog_get_icon (ml_dialog);
+void ml_dialog_clear_buttons (ml_dialog);
+void ml_dialog_add_button (ml_dialog, const char *text, void (*callback_fn) (ml_session, void *), ml_session session, void *data);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A dialog is a widget for asking a user a question, and
+getting an answer. It can also be used for presenting the
+user with confirmation that some operation has been carried
+out, or for presenting the user with an error
+message.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Fundamentally, a dialog consists of some text, and a series
+of zero or more buttons along the bottom of the widget. A
+dialog can also have an optional title which appears along
+the top and an optional icon which appears on the left of
+the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Dialogs are almost always used packed directly into a
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Dialogs are actually &quot;super-widgets&quot;, built up
+from fundamental widgets like labels, buttons, and table
+layouts. However currently none of the fundamental widgets
+are actually exposed through the <b>ml_dialog_*</b>
+interface.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_dialog</b> creates a new dialog widget with no
+text, no title, no icon and no buttons (ie. not very useful!
+- after calling this you should immediately call
+<b>ml_dialog_set_text</b> and probably
+<b>ml_dialog_add_button</b> too).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_text</b> updates the text in a dialog
+widget. Although having text is not strictly mandatory, it
+is highly advisable.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_title</b> changes the title. To have
+no title, set the title to <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_icon</b> changes the icon. To have no
+icon, set the icon to <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_clear_buttons</b> removes all the buttons from
+the dialog.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_add_button</b> adds a single button to the
+dialog. If there are already buttons attached to the dialog,
+then this adds the new button on the right. The <b>text</b>
+which appears on the button must be specified. When the
+button is pressed, <b>callback_fn</b> will be
+called.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_dialog_get_title.3.html b/doc/ml_dialog_get_title.3.html
new file mode 100644 (file)
index 0000000..f5ffbf8
--- /dev/null
@@ -0,0 +1,161 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_dialog</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_dialog</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:52 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_dialog, ml_dialog_set_text, ml_dialog_get_text, ml_dialog_set_title, ml_dialog_get_title, ml_dialog_set_icon, ml_dialog_get_icon, ml_dialog_clear_buttons, ml_dialog_add_button - monolith dialog widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_dialog.h&gt;
+
+ml_dialog new_ml_dialog (pool pool);
+void ml_dialog_set_text (ml_dialog, const char *text);
+const char *ml_dialog_get_text (ml_dialog);
+void ml_dialog_set_title (ml_dialog, const char *title);
+const char *ml_dialog_get_title (ml_dialog);
+void ml_dialog_set_icon (ml_dialog, const char *icon);
+const char *ml_dialog_get_icon (ml_dialog);
+void ml_dialog_clear_buttons (ml_dialog);
+void ml_dialog_add_button (ml_dialog, const char *text, void (*callback_fn) (ml_session, void *), ml_session session, void *data);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A dialog is a widget for asking a user a question, and
+getting an answer. It can also be used for presenting the
+user with confirmation that some operation has been carried
+out, or for presenting the user with an error
+message.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Fundamentally, a dialog consists of some text, and a series
+of zero or more buttons along the bottom of the widget. A
+dialog can also have an optional title which appears along
+the top and an optional icon which appears on the left of
+the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Dialogs are almost always used packed directly into a
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Dialogs are actually &quot;super-widgets&quot;, built up
+from fundamental widgets like labels, buttons, and table
+layouts. However currently none of the fundamental widgets
+are actually exposed through the <b>ml_dialog_*</b>
+interface.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_dialog</b> creates a new dialog widget with no
+text, no title, no icon and no buttons (ie. not very useful!
+- after calling this you should immediately call
+<b>ml_dialog_set_text</b> and probably
+<b>ml_dialog_add_button</b> too).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_text</b> updates the text in a dialog
+widget. Although having text is not strictly mandatory, it
+is highly advisable.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_title</b> changes the title. To have
+no title, set the title to <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_icon</b> changes the icon. To have no
+icon, set the icon to <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_clear_buttons</b> removes all the buttons from
+the dialog.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_add_button</b> adds a single button to the
+dialog. If there are already buttons attached to the dialog,
+then this adds the new button on the right. The <b>text</b>
+which appears on the button must be specified. When the
+button is pressed, <b>callback_fn</b> will be
+called.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_dialog_set_icon.3.html b/doc/ml_dialog_set_icon.3.html
new file mode 100644 (file)
index 0000000..f5ffbf8
--- /dev/null
@@ -0,0 +1,161 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_dialog</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_dialog</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:52 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_dialog, ml_dialog_set_text, ml_dialog_get_text, ml_dialog_set_title, ml_dialog_get_title, ml_dialog_set_icon, ml_dialog_get_icon, ml_dialog_clear_buttons, ml_dialog_add_button - monolith dialog widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_dialog.h&gt;
+
+ml_dialog new_ml_dialog (pool pool);
+void ml_dialog_set_text (ml_dialog, const char *text);
+const char *ml_dialog_get_text (ml_dialog);
+void ml_dialog_set_title (ml_dialog, const char *title);
+const char *ml_dialog_get_title (ml_dialog);
+void ml_dialog_set_icon (ml_dialog, const char *icon);
+const char *ml_dialog_get_icon (ml_dialog);
+void ml_dialog_clear_buttons (ml_dialog);
+void ml_dialog_add_button (ml_dialog, const char *text, void (*callback_fn) (ml_session, void *), ml_session session, void *data);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A dialog is a widget for asking a user a question, and
+getting an answer. It can also be used for presenting the
+user with confirmation that some operation has been carried
+out, or for presenting the user with an error
+message.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Fundamentally, a dialog consists of some text, and a series
+of zero or more buttons along the bottom of the widget. A
+dialog can also have an optional title which appears along
+the top and an optional icon which appears on the left of
+the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Dialogs are almost always used packed directly into a
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Dialogs are actually &quot;super-widgets&quot;, built up
+from fundamental widgets like labels, buttons, and table
+layouts. However currently none of the fundamental widgets
+are actually exposed through the <b>ml_dialog_*</b>
+interface.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_dialog</b> creates a new dialog widget with no
+text, no title, no icon and no buttons (ie. not very useful!
+- after calling this you should immediately call
+<b>ml_dialog_set_text</b> and probably
+<b>ml_dialog_add_button</b> too).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_text</b> updates the text in a dialog
+widget. Although having text is not strictly mandatory, it
+is highly advisable.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_title</b> changes the title. To have
+no title, set the title to <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_icon</b> changes the icon. To have no
+icon, set the icon to <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_clear_buttons</b> removes all the buttons from
+the dialog.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_add_button</b> adds a single button to the
+dialog. If there are already buttons attached to the dialog,
+then this adds the new button on the right. The <b>text</b>
+which appears on the button must be specified. When the
+button is pressed, <b>callback_fn</b> will be
+called.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_dialog_set_text.3.html b/doc/ml_dialog_set_text.3.html
new file mode 100644 (file)
index 0000000..eb92b98
--- /dev/null
@@ -0,0 +1,161 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_dialog</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_dialog</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:53 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_dialog, ml_dialog_set_text, ml_dialog_get_text, ml_dialog_set_title, ml_dialog_get_title, ml_dialog_set_icon, ml_dialog_get_icon, ml_dialog_clear_buttons, ml_dialog_add_button - monolith dialog widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_dialog.h&gt;
+
+ml_dialog new_ml_dialog (pool pool);
+void ml_dialog_set_text (ml_dialog, const char *text);
+const char *ml_dialog_get_text (ml_dialog);
+void ml_dialog_set_title (ml_dialog, const char *title);
+const char *ml_dialog_get_title (ml_dialog);
+void ml_dialog_set_icon (ml_dialog, const char *icon);
+const char *ml_dialog_get_icon (ml_dialog);
+void ml_dialog_clear_buttons (ml_dialog);
+void ml_dialog_add_button (ml_dialog, const char *text, void (*callback_fn) (ml_session, void *), ml_session session, void *data);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A dialog is a widget for asking a user a question, and
+getting an answer. It can also be used for presenting the
+user with confirmation that some operation has been carried
+out, or for presenting the user with an error
+message.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Fundamentally, a dialog consists of some text, and a series
+of zero or more buttons along the bottom of the widget. A
+dialog can also have an optional title which appears along
+the top and an optional icon which appears on the left of
+the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Dialogs are almost always used packed directly into a
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Dialogs are actually &quot;super-widgets&quot;, built up
+from fundamental widgets like labels, buttons, and table
+layouts. However currently none of the fundamental widgets
+are actually exposed through the <b>ml_dialog_*</b>
+interface.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_dialog</b> creates a new dialog widget with no
+text, no title, no icon and no buttons (ie. not very useful!
+- after calling this you should immediately call
+<b>ml_dialog_set_text</b> and probably
+<b>ml_dialog_add_button</b> too).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_text</b> updates the text in a dialog
+widget. Although having text is not strictly mandatory, it
+is highly advisable.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_title</b> changes the title. To have
+no title, set the title to <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_icon</b> changes the icon. To have no
+icon, set the icon to <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_clear_buttons</b> removes all the buttons from
+the dialog.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_add_button</b> adds a single button to the
+dialog. If there are already buttons attached to the dialog,
+then this adds the new button on the right. The <b>text</b>
+which appears on the button must be specified. When the
+button is pressed, <b>callback_fn</b> will be
+called.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_dialog_set_title.3.html b/doc/ml_dialog_set_title.3.html
new file mode 100644 (file)
index 0000000..eb92b98
--- /dev/null
@@ -0,0 +1,161 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_dialog</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_dialog</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:53 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_dialog, ml_dialog_set_text, ml_dialog_get_text, ml_dialog_set_title, ml_dialog_get_title, ml_dialog_set_icon, ml_dialog_get_icon, ml_dialog_clear_buttons, ml_dialog_add_button - monolith dialog widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_dialog.h&gt;
+
+ml_dialog new_ml_dialog (pool pool);
+void ml_dialog_set_text (ml_dialog, const char *text);
+const char *ml_dialog_get_text (ml_dialog);
+void ml_dialog_set_title (ml_dialog, const char *title);
+const char *ml_dialog_get_title (ml_dialog);
+void ml_dialog_set_icon (ml_dialog, const char *icon);
+const char *ml_dialog_get_icon (ml_dialog);
+void ml_dialog_clear_buttons (ml_dialog);
+void ml_dialog_add_button (ml_dialog, const char *text, void (*callback_fn) (ml_session, void *), ml_session session, void *data);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A dialog is a widget for asking a user a question, and
+getting an answer. It can also be used for presenting the
+user with confirmation that some operation has been carried
+out, or for presenting the user with an error
+message.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Fundamentally, a dialog consists of some text, and a series
+of zero or more buttons along the bottom of the widget. A
+dialog can also have an optional title which appears along
+the top and an optional icon which appears on the left of
+the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Dialogs are almost always used packed directly into a
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Dialogs are actually &quot;super-widgets&quot;, built up
+from fundamental widgets like labels, buttons, and table
+layouts. However currently none of the fundamental widgets
+are actually exposed through the <b>ml_dialog_*</b>
+interface.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_dialog</b> creates a new dialog widget with no
+text, no title, no icon and no buttons (ie. not very useful!
+- after calling this you should immediately call
+<b>ml_dialog_set_text</b> and probably
+<b>ml_dialog_add_button</b> too).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_text</b> updates the text in a dialog
+widget. Although having text is not strictly mandatory, it
+is highly advisable.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_title</b> changes the title. To have
+no title, set the title to <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_icon</b> changes the icon. To have no
+icon, set the icon to <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_clear_buttons</b> removes all the buttons from
+the dialog.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_add_button</b> adds a single button to the
+dialog. If there are already buttons attached to the dialog,
+then this adds the new button on the right. The <b>text</b>
+which appears on the button must be specified. When the
+button is pressed, <b>callback_fn</b> will be
+called.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_entry_point.3.html b/doc/ml_entry_point.3.html
new file mode 100644 (file)
index 0000000..c956eb6
--- /dev/null
@@ -0,0 +1,107 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ml_entry_point</title>
+</head>
+<body>
+
+<h1 align=center>ml_entry_point</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:53 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ml_entry_point - entry point into monolith from handle_request</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;monolith.h&gt;
+
+int ml_entry_point (rws_request rq, void (*app_main) (ml_session));
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_entry_point</b> is the entry point into the monolith
+core library code, called from <b>handle_request</b>. The
+application's <b>handle_request</b> function should contain
+just a single call to <b>ml_entry_point</b> like
+this:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>return ml_entry_point (rq, app_main);</b></td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+where <b>rq</b> is the <b>rws_request</b> object passed by
+the web server, and <b>app_main</b> is the application's
+main function.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+See <b>examples/01_label_and_button.c</b> for a simple
+monolith application.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_pool(3)</b>, <b>rws_request_pool(3)</b>,
+<b>new_ml_window(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_flow_layout_clear.3.html b/doc/ml_flow_layout_clear.3.html
new file mode 100644 (file)
index 0000000..abc97bc
--- /dev/null
@@ -0,0 +1,124 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_flow_layout</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_flow_layout</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:53 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_flow_layout, ml_flow_layout_push_back, ml_flow_layout_pop_back, ml_flow_layout_push_front, ml_flow_layout_pop_front, ml_flow_layout_get, ml_flow_layout_insert, ml_flow_layout_replace, ml_flow_layout_erase, ml_flow_layout_clear, ml_flow_layout_size, ml_flow_layout_pack - monolith flow_layout widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_flow_layout.h&gt;
+
+ml_flow_layout new_ml_flow_layout (pool pool);
+void ml_flow_layout_push_back (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_back (ml_flow_layout);
+void ml_flow_layout_push_front (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_front (ml_flow_layout);
+ml_widget ml_flow_layout_get (ml_flow_layout, int i);
+void ml_flow_layout_insert (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_replace (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_erase (ml_flow_layout, int i);
+void ml_flow_layout_clear (ml_flow_layout);
+int ml_flow_layout_size (ml_flow_layout);
+void ml_flow_layout_pack (ml_flow_layout, ml_widget);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A flow layout widget is the simplest type of layout widget.
+It contains an ordered list of widgets, and it simply
+arranges them one after another (in other words with no
+&quot;layout&quot; at all).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_flow_layout</b> creates a new flow layout
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Underlying the flow layout widget is a simple c2lib vector,
+and the other access functions use the same notation as the
+equivalent c2lib <b>vector_*</b> functions. Go to the SEE
+ALSO section below to see how to manipulate widgets within a
+flow layout.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_flow_layout_pack</b> is equivalent to
+<b>ml_flow_layout_push_back</b>: it appends the widget to
+the end of the current vector of widgets.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_flow_layout_erase.3.html b/doc/ml_flow_layout_erase.3.html
new file mode 100644 (file)
index 0000000..17e13a1
--- /dev/null
@@ -0,0 +1,124 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_flow_layout</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_flow_layout</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:54 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_flow_layout, ml_flow_layout_push_back, ml_flow_layout_pop_back, ml_flow_layout_push_front, ml_flow_layout_pop_front, ml_flow_layout_get, ml_flow_layout_insert, ml_flow_layout_replace, ml_flow_layout_erase, ml_flow_layout_clear, ml_flow_layout_size, ml_flow_layout_pack - monolith flow_layout widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_flow_layout.h&gt;
+
+ml_flow_layout new_ml_flow_layout (pool pool);
+void ml_flow_layout_push_back (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_back (ml_flow_layout);
+void ml_flow_layout_push_front (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_front (ml_flow_layout);
+ml_widget ml_flow_layout_get (ml_flow_layout, int i);
+void ml_flow_layout_insert (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_replace (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_erase (ml_flow_layout, int i);
+void ml_flow_layout_clear (ml_flow_layout);
+int ml_flow_layout_size (ml_flow_layout);
+void ml_flow_layout_pack (ml_flow_layout, ml_widget);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A flow layout widget is the simplest type of layout widget.
+It contains an ordered list of widgets, and it simply
+arranges them one after another (in other words with no
+&quot;layout&quot; at all).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_flow_layout</b> creates a new flow layout
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Underlying the flow layout widget is a simple c2lib vector,
+and the other access functions use the same notation as the
+equivalent c2lib <b>vector_*</b> functions. Go to the SEE
+ALSO section below to see how to manipulate widgets within a
+flow layout.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_flow_layout_pack</b> is equivalent to
+<b>ml_flow_layout_push_back</b>: it appends the widget to
+the end of the current vector of widgets.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_flow_layout_get.3.html b/doc/ml_flow_layout_get.3.html
new file mode 100644 (file)
index 0000000..17e13a1
--- /dev/null
@@ -0,0 +1,124 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_flow_layout</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_flow_layout</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:54 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_flow_layout, ml_flow_layout_push_back, ml_flow_layout_pop_back, ml_flow_layout_push_front, ml_flow_layout_pop_front, ml_flow_layout_get, ml_flow_layout_insert, ml_flow_layout_replace, ml_flow_layout_erase, ml_flow_layout_clear, ml_flow_layout_size, ml_flow_layout_pack - monolith flow_layout widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_flow_layout.h&gt;
+
+ml_flow_layout new_ml_flow_layout (pool pool);
+void ml_flow_layout_push_back (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_back (ml_flow_layout);
+void ml_flow_layout_push_front (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_front (ml_flow_layout);
+ml_widget ml_flow_layout_get (ml_flow_layout, int i);
+void ml_flow_layout_insert (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_replace (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_erase (ml_flow_layout, int i);
+void ml_flow_layout_clear (ml_flow_layout);
+int ml_flow_layout_size (ml_flow_layout);
+void ml_flow_layout_pack (ml_flow_layout, ml_widget);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A flow layout widget is the simplest type of layout widget.
+It contains an ordered list of widgets, and it simply
+arranges them one after another (in other words with no
+&quot;layout&quot; at all).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_flow_layout</b> creates a new flow layout
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Underlying the flow layout widget is a simple c2lib vector,
+and the other access functions use the same notation as the
+equivalent c2lib <b>vector_*</b> functions. Go to the SEE
+ALSO section below to see how to manipulate widgets within a
+flow layout.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_flow_layout_pack</b> is equivalent to
+<b>ml_flow_layout_push_back</b>: it appends the widget to
+the end of the current vector of widgets.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_flow_layout_insert.3.html b/doc/ml_flow_layout_insert.3.html
new file mode 100644 (file)
index 0000000..17e13a1
--- /dev/null
@@ -0,0 +1,124 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_flow_layout</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_flow_layout</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:54 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_flow_layout, ml_flow_layout_push_back, ml_flow_layout_pop_back, ml_flow_layout_push_front, ml_flow_layout_pop_front, ml_flow_layout_get, ml_flow_layout_insert, ml_flow_layout_replace, ml_flow_layout_erase, ml_flow_layout_clear, ml_flow_layout_size, ml_flow_layout_pack - monolith flow_layout widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_flow_layout.h&gt;
+
+ml_flow_layout new_ml_flow_layout (pool pool);
+void ml_flow_layout_push_back (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_back (ml_flow_layout);
+void ml_flow_layout_push_front (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_front (ml_flow_layout);
+ml_widget ml_flow_layout_get (ml_flow_layout, int i);
+void ml_flow_layout_insert (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_replace (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_erase (ml_flow_layout, int i);
+void ml_flow_layout_clear (ml_flow_layout);
+int ml_flow_layout_size (ml_flow_layout);
+void ml_flow_layout_pack (ml_flow_layout, ml_widget);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A flow layout widget is the simplest type of layout widget.
+It contains an ordered list of widgets, and it simply
+arranges them one after another (in other words with no
+&quot;layout&quot; at all).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_flow_layout</b> creates a new flow layout
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Underlying the flow layout widget is a simple c2lib vector,
+and the other access functions use the same notation as the
+equivalent c2lib <b>vector_*</b> functions. Go to the SEE
+ALSO section below to see how to manipulate widgets within a
+flow layout.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_flow_layout_pack</b> is equivalent to
+<b>ml_flow_layout_push_back</b>: it appends the widget to
+the end of the current vector of widgets.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_flow_layout_pack.3.html b/doc/ml_flow_layout_pack.3.html
new file mode 100644 (file)
index 0000000..17e13a1
--- /dev/null
@@ -0,0 +1,124 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_flow_layout</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_flow_layout</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:54 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_flow_layout, ml_flow_layout_push_back, ml_flow_layout_pop_back, ml_flow_layout_push_front, ml_flow_layout_pop_front, ml_flow_layout_get, ml_flow_layout_insert, ml_flow_layout_replace, ml_flow_layout_erase, ml_flow_layout_clear, ml_flow_layout_size, ml_flow_layout_pack - monolith flow_layout widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_flow_layout.h&gt;
+
+ml_flow_layout new_ml_flow_layout (pool pool);
+void ml_flow_layout_push_back (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_back (ml_flow_layout);
+void ml_flow_layout_push_front (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_front (ml_flow_layout);
+ml_widget ml_flow_layout_get (ml_flow_layout, int i);
+void ml_flow_layout_insert (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_replace (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_erase (ml_flow_layout, int i);
+void ml_flow_layout_clear (ml_flow_layout);
+int ml_flow_layout_size (ml_flow_layout);
+void ml_flow_layout_pack (ml_flow_layout, ml_widget);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A flow layout widget is the simplest type of layout widget.
+It contains an ordered list of widgets, and it simply
+arranges them one after another (in other words with no
+&quot;layout&quot; at all).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_flow_layout</b> creates a new flow layout
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Underlying the flow layout widget is a simple c2lib vector,
+and the other access functions use the same notation as the
+equivalent c2lib <b>vector_*</b> functions. Go to the SEE
+ALSO section below to see how to manipulate widgets within a
+flow layout.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_flow_layout_pack</b> is equivalent to
+<b>ml_flow_layout_push_back</b>: it appends the widget to
+the end of the current vector of widgets.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_flow_layout_pop_back.3.html b/doc/ml_flow_layout_pop_back.3.html
new file mode 100644 (file)
index 0000000..608dc89
--- /dev/null
@@ -0,0 +1,124 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_flow_layout</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_flow_layout</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:55 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_flow_layout, ml_flow_layout_push_back, ml_flow_layout_pop_back, ml_flow_layout_push_front, ml_flow_layout_pop_front, ml_flow_layout_get, ml_flow_layout_insert, ml_flow_layout_replace, ml_flow_layout_erase, ml_flow_layout_clear, ml_flow_layout_size, ml_flow_layout_pack - monolith flow_layout widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_flow_layout.h&gt;
+
+ml_flow_layout new_ml_flow_layout (pool pool);
+void ml_flow_layout_push_back (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_back (ml_flow_layout);
+void ml_flow_layout_push_front (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_front (ml_flow_layout);
+ml_widget ml_flow_layout_get (ml_flow_layout, int i);
+void ml_flow_layout_insert (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_replace (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_erase (ml_flow_layout, int i);
+void ml_flow_layout_clear (ml_flow_layout);
+int ml_flow_layout_size (ml_flow_layout);
+void ml_flow_layout_pack (ml_flow_layout, ml_widget);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A flow layout widget is the simplest type of layout widget.
+It contains an ordered list of widgets, and it simply
+arranges them one after another (in other words with no
+&quot;layout&quot; at all).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_flow_layout</b> creates a new flow layout
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Underlying the flow layout widget is a simple c2lib vector,
+and the other access functions use the same notation as the
+equivalent c2lib <b>vector_*</b> functions. Go to the SEE
+ALSO section below to see how to manipulate widgets within a
+flow layout.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_flow_layout_pack</b> is equivalent to
+<b>ml_flow_layout_push_back</b>: it appends the widget to
+the end of the current vector of widgets.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_flow_layout_pop_front.3.html b/doc/ml_flow_layout_pop_front.3.html
new file mode 100644 (file)
index 0000000..608dc89
--- /dev/null
@@ -0,0 +1,124 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_flow_layout</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_flow_layout</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:55 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_flow_layout, ml_flow_layout_push_back, ml_flow_layout_pop_back, ml_flow_layout_push_front, ml_flow_layout_pop_front, ml_flow_layout_get, ml_flow_layout_insert, ml_flow_layout_replace, ml_flow_layout_erase, ml_flow_layout_clear, ml_flow_layout_size, ml_flow_layout_pack - monolith flow_layout widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_flow_layout.h&gt;
+
+ml_flow_layout new_ml_flow_layout (pool pool);
+void ml_flow_layout_push_back (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_back (ml_flow_layout);
+void ml_flow_layout_push_front (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_front (ml_flow_layout);
+ml_widget ml_flow_layout_get (ml_flow_layout, int i);
+void ml_flow_layout_insert (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_replace (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_erase (ml_flow_layout, int i);
+void ml_flow_layout_clear (ml_flow_layout);
+int ml_flow_layout_size (ml_flow_layout);
+void ml_flow_layout_pack (ml_flow_layout, ml_widget);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A flow layout widget is the simplest type of layout widget.
+It contains an ordered list of widgets, and it simply
+arranges them one after another (in other words with no
+&quot;layout&quot; at all).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_flow_layout</b> creates a new flow layout
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Underlying the flow layout widget is a simple c2lib vector,
+and the other access functions use the same notation as the
+equivalent c2lib <b>vector_*</b> functions. Go to the SEE
+ALSO section below to see how to manipulate widgets within a
+flow layout.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_flow_layout_pack</b> is equivalent to
+<b>ml_flow_layout_push_back</b>: it appends the widget to
+the end of the current vector of widgets.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_flow_layout_push_back.3.html b/doc/ml_flow_layout_push_back.3.html
new file mode 100644 (file)
index 0000000..608dc89
--- /dev/null
@@ -0,0 +1,124 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_flow_layout</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_flow_layout</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:55 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_flow_layout, ml_flow_layout_push_back, ml_flow_layout_pop_back, ml_flow_layout_push_front, ml_flow_layout_pop_front, ml_flow_layout_get, ml_flow_layout_insert, ml_flow_layout_replace, ml_flow_layout_erase, ml_flow_layout_clear, ml_flow_layout_size, ml_flow_layout_pack - monolith flow_layout widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_flow_layout.h&gt;
+
+ml_flow_layout new_ml_flow_layout (pool pool);
+void ml_flow_layout_push_back (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_back (ml_flow_layout);
+void ml_flow_layout_push_front (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_front (ml_flow_layout);
+ml_widget ml_flow_layout_get (ml_flow_layout, int i);
+void ml_flow_layout_insert (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_replace (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_erase (ml_flow_layout, int i);
+void ml_flow_layout_clear (ml_flow_layout);
+int ml_flow_layout_size (ml_flow_layout);
+void ml_flow_layout_pack (ml_flow_layout, ml_widget);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A flow layout widget is the simplest type of layout widget.
+It contains an ordered list of widgets, and it simply
+arranges them one after another (in other words with no
+&quot;layout&quot; at all).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_flow_layout</b> creates a new flow layout
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Underlying the flow layout widget is a simple c2lib vector,
+and the other access functions use the same notation as the
+equivalent c2lib <b>vector_*</b> functions. Go to the SEE
+ALSO section below to see how to manipulate widgets within a
+flow layout.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_flow_layout_pack</b> is equivalent to
+<b>ml_flow_layout_push_back</b>: it appends the widget to
+the end of the current vector of widgets.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_flow_layout_push_front.3.html b/doc/ml_flow_layout_push_front.3.html
new file mode 100644 (file)
index 0000000..608dc89
--- /dev/null
@@ -0,0 +1,124 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_flow_layout</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_flow_layout</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:55 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_flow_layout, ml_flow_layout_push_back, ml_flow_layout_pop_back, ml_flow_layout_push_front, ml_flow_layout_pop_front, ml_flow_layout_get, ml_flow_layout_insert, ml_flow_layout_replace, ml_flow_layout_erase, ml_flow_layout_clear, ml_flow_layout_size, ml_flow_layout_pack - monolith flow_layout widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_flow_layout.h&gt;
+
+ml_flow_layout new_ml_flow_layout (pool pool);
+void ml_flow_layout_push_back (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_back (ml_flow_layout);
+void ml_flow_layout_push_front (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_front (ml_flow_layout);
+ml_widget ml_flow_layout_get (ml_flow_layout, int i);
+void ml_flow_layout_insert (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_replace (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_erase (ml_flow_layout, int i);
+void ml_flow_layout_clear (ml_flow_layout);
+int ml_flow_layout_size (ml_flow_layout);
+void ml_flow_layout_pack (ml_flow_layout, ml_widget);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A flow layout widget is the simplest type of layout widget.
+It contains an ordered list of widgets, and it simply
+arranges them one after another (in other words with no
+&quot;layout&quot; at all).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_flow_layout</b> creates a new flow layout
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Underlying the flow layout widget is a simple c2lib vector,
+and the other access functions use the same notation as the
+equivalent c2lib <b>vector_*</b> functions. Go to the SEE
+ALSO section below to see how to manipulate widgets within a
+flow layout.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_flow_layout_pack</b> is equivalent to
+<b>ml_flow_layout_push_back</b>: it appends the widget to
+the end of the current vector of widgets.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_flow_layout_replace.3.html b/doc/ml_flow_layout_replace.3.html
new file mode 100644 (file)
index 0000000..608dc89
--- /dev/null
@@ -0,0 +1,124 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_flow_layout</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_flow_layout</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:55 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_flow_layout, ml_flow_layout_push_back, ml_flow_layout_pop_back, ml_flow_layout_push_front, ml_flow_layout_pop_front, ml_flow_layout_get, ml_flow_layout_insert, ml_flow_layout_replace, ml_flow_layout_erase, ml_flow_layout_clear, ml_flow_layout_size, ml_flow_layout_pack - monolith flow_layout widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_flow_layout.h&gt;
+
+ml_flow_layout new_ml_flow_layout (pool pool);
+void ml_flow_layout_push_back (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_back (ml_flow_layout);
+void ml_flow_layout_push_front (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_front (ml_flow_layout);
+ml_widget ml_flow_layout_get (ml_flow_layout, int i);
+void ml_flow_layout_insert (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_replace (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_erase (ml_flow_layout, int i);
+void ml_flow_layout_clear (ml_flow_layout);
+int ml_flow_layout_size (ml_flow_layout);
+void ml_flow_layout_pack (ml_flow_layout, ml_widget);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A flow layout widget is the simplest type of layout widget.
+It contains an ordered list of widgets, and it simply
+arranges them one after another (in other words with no
+&quot;layout&quot; at all).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_flow_layout</b> creates a new flow layout
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Underlying the flow layout widget is a simple c2lib vector,
+and the other access functions use the same notation as the
+equivalent c2lib <b>vector_*</b> functions. Go to the SEE
+ALSO section below to see how to manipulate widgets within a
+flow layout.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_flow_layout_pack</b> is equivalent to
+<b>ml_flow_layout_push_back</b>: it appends the widget to
+the end of the current vector of widgets.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_flow_layout_size.3.html b/doc/ml_flow_layout_size.3.html
new file mode 100644 (file)
index 0000000..556009f
--- /dev/null
@@ -0,0 +1,124 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_flow_layout</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_flow_layout</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:56 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_flow_layout, ml_flow_layout_push_back, ml_flow_layout_pop_back, ml_flow_layout_push_front, ml_flow_layout_pop_front, ml_flow_layout_get, ml_flow_layout_insert, ml_flow_layout_replace, ml_flow_layout_erase, ml_flow_layout_clear, ml_flow_layout_size, ml_flow_layout_pack - monolith flow_layout widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_flow_layout.h&gt;
+
+ml_flow_layout new_ml_flow_layout (pool pool);
+void ml_flow_layout_push_back (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_back (ml_flow_layout);
+void ml_flow_layout_push_front (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_front (ml_flow_layout);
+ml_widget ml_flow_layout_get (ml_flow_layout, int i);
+void ml_flow_layout_insert (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_replace (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_erase (ml_flow_layout, int i);
+void ml_flow_layout_clear (ml_flow_layout);
+int ml_flow_layout_size (ml_flow_layout);
+void ml_flow_layout_pack (ml_flow_layout, ml_widget);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A flow layout widget is the simplest type of layout widget.
+It contains an ordered list of widgets, and it simply
+arranges them one after another (in other words with no
+&quot;layout&quot; at all).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_flow_layout</b> creates a new flow layout
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Underlying the flow layout widget is a simple c2lib vector,
+and the other access functions use the same notation as the
+equivalent c2lib <b>vector_*</b> functions. Go to the SEE
+ALSO section below to see how to manipulate widgets within a
+flow layout.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_flow_layout_pack</b> is equivalent to
+<b>ml_flow_layout_push_back</b>: it appends the widget to
+the end of the current vector of widgets.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_input_clear_value.3.html b/doc/ml_form_input_clear_value.3.html
new file mode 100644 (file)
index 0000000..b09abbd
--- /dev/null
@@ -0,0 +1,82 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ml_form_input_set_value</title>
+</head>
+<body>
+
+<h1 align=center>ml_form_input_set_value</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:56 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ml_form_input_set_value, ml_form_input_get_value, ml_form_input_clear_value - Operations on generic form inputs.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_input.h&gt;
+
+void ml_form_input_set_value (ml_form_input form_input, const char *value);
+const char *ml_form_input_get_value (ml_form_input form_input);
+void ml_form_input_clear_value (ml_form_input form_input);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_input_(set|get)_value</b> update the value field
+in any form input.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_input_clear_value</b> clears (nulls) the value
+field.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_input_get_value.3.html b/doc/ml_form_input_get_value.3.html
new file mode 100644 (file)
index 0000000..b09abbd
--- /dev/null
@@ -0,0 +1,82 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ml_form_input_set_value</title>
+</head>
+<body>
+
+<h1 align=center>ml_form_input_set_value</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:56 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ml_form_input_set_value, ml_form_input_get_value, ml_form_input_clear_value - Operations on generic form inputs.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_input.h&gt;
+
+void ml_form_input_set_value (ml_form_input form_input, const char *value);
+const char *ml_form_input_get_value (ml_form_input form_input);
+void ml_form_input_clear_value (ml_form_input form_input);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_input_(set|get)_value</b> update the value field
+in any form input.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_input_clear_value</b> clears (nulls) the value
+field.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_input_set_value.3.html b/doc/ml_form_input_set_value.3.html
new file mode 100644 (file)
index 0000000..b09abbd
--- /dev/null
@@ -0,0 +1,82 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ml_form_input_set_value</title>
+</head>
+<body>
+
+<h1 align=center>ml_form_input_set_value</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:56 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ml_form_input_set_value, ml_form_input_get_value, ml_form_input_clear_value - Operations on generic form inputs.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_input.h&gt;
+
+void ml_form_input_set_value (ml_form_input form_input, const char *value);
+const char *ml_form_input_get_value (ml_form_input form_input);
+void ml_form_input_clear_value (ml_form_input form_input);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_input_(set|get)_value</b> update the value field
+in any form input.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_input_clear_value</b> clears (nulls) the value
+field.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_radio_get_checked.3.html b/doc/ml_form_radio_get_checked.3.html
new file mode 100644 (file)
index 0000000..f815906
--- /dev/null
@@ -0,0 +1,107 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_radio</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_radio</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:57 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_radio, ml_form_radio_set_checked, ml_form_radio_get_checked - monolith radio button widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_radio.h&gt;
+
+ml_form_radio new_ml_form_radio (pool pool, ml_form_radio_group group, const char *value);
+void ml_form_radio_set_checked (ml_form_radio w, int checked);
+int ml_form_radio_get_checked (ml_form_radio w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a radio button input, for use in forms. Radio
+buttons represent choices, and thus are grouped together.
+For this reason, you must place all related radio buttons
+inside a <b>ml_form_radio_group</b> widget, which itself
+goes inside the form.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_radio</b> creates a new radio button widget.
+The radio button group into which this widget is being
+embedded is passed as the <b>group</b>
+parameter.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+After the form has been submitted, you can see which button
+was pressed by checking the value of the
+<b>ml_form_radio_group</b> widget, or by looking at the
+checked status of each individual radio button using
+<b>ml_form_radio_(set|get)_checked</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>,
+<b>new_ml_form_radio_group(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_radio_group_pack.3.html b/doc/ml_form_radio_group_pack.3.html
new file mode 100644 (file)
index 0000000..89070eb
--- /dev/null
@@ -0,0 +1,105 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_radio_group</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_radio_group</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:57 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_radio_group, ml_form_radio_group_pack - monolith group of radio buttons widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_radio_group.h&gt;
+
+ml_form_radio_group new_ml_form_radio_group (pool pool, ml_form form);
+void ml_form_radio_group_pack (ml_form_radio_group w, ml_widget);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A radio group is a widget which contains a group of related
+radio buttons. You cannot use radio buttons
+&quot;naked&quot; in a form, but instead must embed related
+buttons inside one of these widgets.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_radio_group</b> creates a new group
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_radio_group_pack</b> packs a single widget inside
+the radio group widget. A radio group can only contain a
+single widget, so if you call pack again, it will forget the
+previous widget. It is recommended that you pack either a
+flow layout or a table layout directly inside the radio
+group, and then pack the actual radio buttons inside
+that.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>new_ml_form_radio(3)</b>,
+<b>ml_form_input_get_value(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_radio_set_checked.3.html b/doc/ml_form_radio_set_checked.3.html
new file mode 100644 (file)
index 0000000..f815906
--- /dev/null
@@ -0,0 +1,107 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_radio</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_radio</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:57 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_radio, ml_form_radio_set_checked, ml_form_radio_get_checked - monolith radio button widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_radio.h&gt;
+
+ml_form_radio new_ml_form_radio (pool pool, ml_form_radio_group group, const char *value);
+void ml_form_radio_set_checked (ml_form_radio w, int checked);
+int ml_form_radio_get_checked (ml_form_radio w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a radio button input, for use in forms. Radio
+buttons represent choices, and thus are grouped together.
+For this reason, you must place all related radio buttons
+inside a <b>ml_form_radio_group</b> widget, which itself
+goes inside the form.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_radio</b> creates a new radio button widget.
+The radio button group into which this widget is being
+embedded is passed as the <b>group</b>
+parameter.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+After the form has been submitted, you can see which button
+was pressed by checking the value of the
+<b>ml_form_radio_group</b> widget, or by looking at the
+checked status of each individual radio button using
+<b>ml_form_radio_(set|get)_checked</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>,
+<b>new_ml_form_radio_group(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_select_clear.3.html b/doc/ml_form_select_clear.3.html
new file mode 100644 (file)
index 0000000..dc236a8
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:57 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_select_erase.3.html b/doc/ml_form_select_erase.3.html
new file mode 100644 (file)
index 0000000..603cd33
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:58 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_select_get.3.html b/doc/ml_form_select_get.3.html
new file mode 100644 (file)
index 0000000..603cd33
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:58 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_select_get_multiple.3.html b/doc/ml_form_select_get_multiple.3.html
new file mode 100644 (file)
index 0000000..603cd33
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:58 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_select_get_selection.3.html b/doc/ml_form_select_get_selection.3.html
new file mode 100644 (file)
index 0000000..603cd33
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:58 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_select_get_selections.3.html b/doc/ml_form_select_get_selections.3.html
new file mode 100644 (file)
index 0000000..832bb1f
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:59 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_select_get_size.3.html b/doc/ml_form_select_get_size.3.html
new file mode 100644 (file)
index 0000000..832bb1f
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:59 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_select_insert.3.html b/doc/ml_form_select_insert.3.html
new file mode 100644 (file)
index 0000000..832bb1f
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:59 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_select_pop_back.3.html b/doc/ml_form_select_pop_back.3.html
new file mode 100644 (file)
index 0000000..832bb1f
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:05:59 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_select_pop_front.3.html b/doc/ml_form_select_pop_front.3.html
new file mode 100644 (file)
index 0000000..b8a05c3
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:00 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_select_push_back.3.html b/doc/ml_form_select_push_back.3.html
new file mode 100644 (file)
index 0000000..b8a05c3
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:00 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_select_push_front.3.html b/doc/ml_form_select_push_front.3.html
new file mode 100644 (file)
index 0000000..b8a05c3
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:00 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_select_replace.3.html b/doc/ml_form_select_replace.3.html
new file mode 100644 (file)
index 0000000..b8a05c3
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:00 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_select_set_multiple.3.html b/doc/ml_form_select_set_multiple.3.html
new file mode 100644 (file)
index 0000000..08fa4d7
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:01 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_select_set_selection.3.html b/doc/ml_form_select_set_selection.3.html
new file mode 100644 (file)
index 0000000..08fa4d7
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:01 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_select_set_selections.3.html b/doc/ml_form_select_set_selections.3.html
new file mode 100644 (file)
index 0000000..08fa4d7
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:01 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_select_set_size.3.html b/doc/ml_form_select_set_size.3.html
new file mode 100644 (file)
index 0000000..08fa4d7
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:01 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_form_select_size.3.html b/doc/ml_form_select_size.3.html
new file mode 100644 (file)
index 0000000..e974731
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:02 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_frameset_get_title.3.html b/doc/ml_frameset_get_title.3.html
new file mode 100644 (file)
index 0000000..1780ab2
--- /dev/null
@@ -0,0 +1,205 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_window</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_window</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:02 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_window, ml_window_pack, ml_window_set_title, ml_window_get_title, ml_window_set_stylesheet, ml_window_get_stylesheet, ml_window_set_charset, ml_window_get_charset, new_ml_frameset, ml_frameset_set_description, ml_frameset_set_title, ml_frameset_get_title - monolith window and frameset</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_window.h&gt;
+
+ml_window new_ml_window (struct ml_session *, pool pool);
+void ml_window_pack (ml_window, ml_widget);
+void ml_window_set_title (ml_window, const char *title);
+const char *ml_window_get_title (ml_window);
+void ml_window_set_stylesheet (ml_window, const char *stylesheet);
+const char *ml_window_get_stylesheet (ml_window);
+void ml_window_set_charset (ml_window, const char *charset);
+const char *ml_window_get_charset (ml_window);
+ml_window new_ml_frameset (struct ml_session *, pool pool, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_description (ml_window, struct ml_session *, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_title (ml_window, const char *);
+const char *ml_frameset_get_title (ml_window);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;window&quot; is a top-level window. Every
+application has at least one window, created in the main
+function. A window is just a wrapper. To actually do
+anything, you must pack a single widget inside the window.
+Windows can only take a single widget. If you want more than
+one widget to appear inside a window, then you must pack
+them into some sort of layout widget first, and pack the
+layout widget into the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;frameset&quot; is used to create framesets (several
+ordinary windows packed into one top-level window, with
+independent scrolling capabilities). Windows and framesets
+are actually so similar, that I have included them in the
+same class (and also so that other code can deal with an
+opaque <b>ml_window</b> pointer and not have to worry about
+whether it is a window or a frameset). Framesets may contain
+windows or other framesets, or a mixture of
+both.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Monolith windows are not widgets (unlike many of the other
+classes in monolith).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_window</b> creates a new monolith
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_pack</b> packs a widget into the window. Since
+a window can only contain a single widget, subsequent calls
+to this function overwrite the packed widget. (Note: this
+call does not apply to framesets).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_title</b> changes the title of the
+window. The title of the window defaults to no title at all,
+so it is a good idea to set this.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_stylesheet</b> changes the stylesheet
+of the page. Monolith default stylesheets are installed in
+<b>/ml-styles/</b>, and the default stylesheet is
+<b>/ml-styles/default.css</b> (supplied in the monolith
+distribution). Stylesheets are used to 'theme' monolith
+applications.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_charset</b> changes the character
+encoding associated with the window. Because of limitations
+in HTML, only a single charset can be associated with all of
+the widgets in a window. The default charset is
+<b>iso-8859-1</b>. For multilingual support, it is
+recommended that users change the charset to either their
+native language encoding, or to <b>utf-8</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_frameset</b> creates a new frameset. <b>rows</b>
+and <b>cols</b> define the number of frames and their
+layout. You can use <b>rows</b> and <b>cols</b> to create
+frameset layouts including grids, as described in the HTML 4
+standard (<b>http://www.w3.org/TR/html401/</b>). To create
+nested framesets, use a frame which generates a frameset
+instead of a window. <b>frames</b> is a vector of <b>struct
+ml_frame_description</b> structures (note: the structures
+themselves, not pointers to the structures). <b>struct
+ml_frame_description</b> contains the following
+fields:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fn</b>: The function which is called generate the frame
+(or nested frameset). This function must call either
+<b>new_ml_window</b> or <b>new_ml_frameset</b>. The function
+is prototyped as <b>void fn (ml_session session, void
+*data);</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>data</b>: Data pointer passed to this
+function.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_set_description</b> allows the frameset
+description to be updated.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_(set|get)_title</b> updates the window
+title.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_frameset_set_description.3.html b/doc/ml_frameset_set_description.3.html
new file mode 100644 (file)
index 0000000..1780ab2
--- /dev/null
@@ -0,0 +1,205 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_window</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_window</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:02 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_window, ml_window_pack, ml_window_set_title, ml_window_get_title, ml_window_set_stylesheet, ml_window_get_stylesheet, ml_window_set_charset, ml_window_get_charset, new_ml_frameset, ml_frameset_set_description, ml_frameset_set_title, ml_frameset_get_title - monolith window and frameset</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_window.h&gt;
+
+ml_window new_ml_window (struct ml_session *, pool pool);
+void ml_window_pack (ml_window, ml_widget);
+void ml_window_set_title (ml_window, const char *title);
+const char *ml_window_get_title (ml_window);
+void ml_window_set_stylesheet (ml_window, const char *stylesheet);
+const char *ml_window_get_stylesheet (ml_window);
+void ml_window_set_charset (ml_window, const char *charset);
+const char *ml_window_get_charset (ml_window);
+ml_window new_ml_frameset (struct ml_session *, pool pool, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_description (ml_window, struct ml_session *, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_title (ml_window, const char *);
+const char *ml_frameset_get_title (ml_window);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;window&quot; is a top-level window. Every
+application has at least one window, created in the main
+function. A window is just a wrapper. To actually do
+anything, you must pack a single widget inside the window.
+Windows can only take a single widget. If you want more than
+one widget to appear inside a window, then you must pack
+them into some sort of layout widget first, and pack the
+layout widget into the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;frameset&quot; is used to create framesets (several
+ordinary windows packed into one top-level window, with
+independent scrolling capabilities). Windows and framesets
+are actually so similar, that I have included them in the
+same class (and also so that other code can deal with an
+opaque <b>ml_window</b> pointer and not have to worry about
+whether it is a window or a frameset). Framesets may contain
+windows or other framesets, or a mixture of
+both.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Monolith windows are not widgets (unlike many of the other
+classes in monolith).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_window</b> creates a new monolith
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_pack</b> packs a widget into the window. Since
+a window can only contain a single widget, subsequent calls
+to this function overwrite the packed widget. (Note: this
+call does not apply to framesets).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_title</b> changes the title of the
+window. The title of the window defaults to no title at all,
+so it is a good idea to set this.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_stylesheet</b> changes the stylesheet
+of the page. Monolith default stylesheets are installed in
+<b>/ml-styles/</b>, and the default stylesheet is
+<b>/ml-styles/default.css</b> (supplied in the monolith
+distribution). Stylesheets are used to 'theme' monolith
+applications.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_charset</b> changes the character
+encoding associated with the window. Because of limitations
+in HTML, only a single charset can be associated with all of
+the widgets in a window. The default charset is
+<b>iso-8859-1</b>. For multilingual support, it is
+recommended that users change the charset to either their
+native language encoding, or to <b>utf-8</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_frameset</b> creates a new frameset. <b>rows</b>
+and <b>cols</b> define the number of frames and their
+layout. You can use <b>rows</b> and <b>cols</b> to create
+frameset layouts including grids, as described in the HTML 4
+standard (<b>http://www.w3.org/TR/html401/</b>). To create
+nested framesets, use a frame which generates a frameset
+instead of a window. <b>frames</b> is a vector of <b>struct
+ml_frame_description</b> structures (note: the structures
+themselves, not pointers to the structures). <b>struct
+ml_frame_description</b> contains the following
+fields:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fn</b>: The function which is called generate the frame
+(or nested frameset). This function must call either
+<b>new_ml_window</b> or <b>new_ml_frameset</b>. The function
+is prototyped as <b>void fn (ml_session session, void
+*data);</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>data</b>: Data pointer passed to this
+function.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_set_description</b> allows the frameset
+description to be updated.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_(set|get)_title</b> updates the window
+title.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_frameset_set_title.3.html b/doc/ml_frameset_set_title.3.html
new file mode 100644 (file)
index 0000000..1780ab2
--- /dev/null
@@ -0,0 +1,205 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_window</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_window</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:02 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_window, ml_window_pack, ml_window_set_title, ml_window_get_title, ml_window_set_stylesheet, ml_window_get_stylesheet, ml_window_set_charset, ml_window_get_charset, new_ml_frameset, ml_frameset_set_description, ml_frameset_set_title, ml_frameset_get_title - monolith window and frameset</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_window.h&gt;
+
+ml_window new_ml_window (struct ml_session *, pool pool);
+void ml_window_pack (ml_window, ml_widget);
+void ml_window_set_title (ml_window, const char *title);
+const char *ml_window_get_title (ml_window);
+void ml_window_set_stylesheet (ml_window, const char *stylesheet);
+const char *ml_window_get_stylesheet (ml_window);
+void ml_window_set_charset (ml_window, const char *charset);
+const char *ml_window_get_charset (ml_window);
+ml_window new_ml_frameset (struct ml_session *, pool pool, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_description (ml_window, struct ml_session *, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_title (ml_window, const char *);
+const char *ml_frameset_get_title (ml_window);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;window&quot; is a top-level window. Every
+application has at least one window, created in the main
+function. A window is just a wrapper. To actually do
+anything, you must pack a single widget inside the window.
+Windows can only take a single widget. If you want more than
+one widget to appear inside a window, then you must pack
+them into some sort of layout widget first, and pack the
+layout widget into the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;frameset&quot; is used to create framesets (several
+ordinary windows packed into one top-level window, with
+independent scrolling capabilities). Windows and framesets
+are actually so similar, that I have included them in the
+same class (and also so that other code can deal with an
+opaque <b>ml_window</b> pointer and not have to worry about
+whether it is a window or a frameset). Framesets may contain
+windows or other framesets, or a mixture of
+both.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Monolith windows are not widgets (unlike many of the other
+classes in monolith).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_window</b> creates a new monolith
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_pack</b> packs a widget into the window. Since
+a window can only contain a single widget, subsequent calls
+to this function overwrite the packed widget. (Note: this
+call does not apply to framesets).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_title</b> changes the title of the
+window. The title of the window defaults to no title at all,
+so it is a good idea to set this.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_stylesheet</b> changes the stylesheet
+of the page. Monolith default stylesheets are installed in
+<b>/ml-styles/</b>, and the default stylesheet is
+<b>/ml-styles/default.css</b> (supplied in the monolith
+distribution). Stylesheets are used to 'theme' monolith
+applications.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_charset</b> changes the character
+encoding associated with the window. Because of limitations
+in HTML, only a single charset can be associated with all of
+the widgets in a window. The default charset is
+<b>iso-8859-1</b>. For multilingual support, it is
+recommended that users change the charset to either their
+native language encoding, or to <b>utf-8</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_frameset</b> creates a new frameset. <b>rows</b>
+and <b>cols</b> define the number of frames and their
+layout. You can use <b>rows</b> and <b>cols</b> to create
+frameset layouts including grids, as described in the HTML 4
+standard (<b>http://www.w3.org/TR/html401/</b>). To create
+nested framesets, use a frame which generates a frameset
+instead of a window. <b>frames</b> is a vector of <b>struct
+ml_frame_description</b> structures (note: the structures
+themselves, not pointers to the structures). <b>struct
+ml_frame_description</b> contains the following
+fields:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fn</b>: The function which is called generate the frame
+(or nested frameset). This function must call either
+<b>new_ml_window</b> or <b>new_ml_frameset</b>. The function
+is prototyped as <b>void fn (ml_session session, void
+*data);</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>data</b>: Data pointer passed to this
+function.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_set_description</b> allows the frameset
+description to be updated.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_(set|get)_title</b> updates the window
+title.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_image_get_src.3.html b/doc/ml_image_get_src.3.html
new file mode 100644 (file)
index 0000000..c669a4a
--- /dev/null
@@ -0,0 +1,101 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_image</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_image</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:03 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_image, ml_image_set_src, ml_image_get_src - monolith image widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_image.h&gt;
+
+ml_image new_ml_image (pool pool, const char *src);
+void ml_image_set_src (ml_image, const char *src);
+const char *ml_image_get_src (ml_image);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+An image widget displays a graphical image, taken from a
+particular source (in fact, it corresponds almost exactly to
+the HTML <b>&lt;img/&gt;</b> element).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The current implementation of <b>ml_image</b> is rather
+immature. In future we will support image sizes, alt, title,
+longdesc, and so on.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_image</b> creates a new image. You should supply
+the <b>src</b> (source) for the image, which is an absolute
+or relative link. If <b>src</b> is set to <b>NULL</b> then
+no image is displayed.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_image_(set|get)_src</b> updates the source of the
+image. You may set the source to <b>NULL</b> to display no
+image at all.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_image_set_src.3.html b/doc/ml_image_set_src.3.html
new file mode 100644 (file)
index 0000000..c669a4a
--- /dev/null
@@ -0,0 +1,101 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_image</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_image</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:03 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_image, ml_image_set_src, ml_image_get_src - monolith image widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_image.h&gt;
+
+ml_image new_ml_image (pool pool, const char *src);
+void ml_image_set_src (ml_image, const char *src);
+const char *ml_image_get_src (ml_image);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+An image widget displays a graphical image, taken from a
+particular source (in fact, it corresponds almost exactly to
+the HTML <b>&lt;img/&gt;</b> element).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The current implementation of <b>ml_image</b> is rather
+immature. In future we will support image sizes, alt, title,
+longdesc, and so on.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_image</b> creates a new image. You should supply
+the <b>src</b> (source) for the image, which is an absolute
+or relative link. If <b>src</b> is set to <b>NULL</b> then
+no image is displayed.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_image_(set|get)_src</b> updates the source of the
+image. You may set the source to <b>NULL</b> to display no
+image at all.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_label_get_text.3.html b/doc/ml_label_get_text.3.html
new file mode 100644 (file)
index 0000000..91a02e5
--- /dev/null
@@ -0,0 +1,91 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_label</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_label</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:03 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_label, ml_label_set_text, ml_label_get_text - monolith label widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_label.h&gt;
+
+ml_label new_ml_label (pool pool, const char *text);
+void ml_label_set_text (ml_label, const char *text);
+const char *ml_label_get_text (ml_label);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A label widget is a generic piece of HTML.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_label</b> creates a new label widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_label_(set|get)_text</b> updates the HTML associated
+with the label. Note that the text string must be either
+static, or already allocated in the label's pool, or
+allocated in a pool with a longer lifetime than the label.
+If the text is set to <b>NULL</b> then this has the same
+effect as setting the text to the empty string.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_label_set_text.3.html b/doc/ml_label_set_text.3.html
new file mode 100644 (file)
index 0000000..91a02e5
--- /dev/null
@@ -0,0 +1,91 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_label</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_label</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:03 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_label, ml_label_set_text, ml_label_get_text - monolith label widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_label.h&gt;
+
+ml_label new_ml_label (pool pool, const char *text);
+void ml_label_set_text (ml_label, const char *text);
+const char *ml_label_get_text (ml_label);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A label widget is a generic piece of HTML.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_label</b> creates a new label widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_label_(set|get)_text</b> updates the HTML associated
+with the label. Note that the text string must be either
+static, or already allocated in the label's pool, or
+allocated in a pool with a longer lifetime than the label.
+If the text is set to <b>NULL</b> then this has the same
+effect as setting the text to the empty string.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_register_action.3.html b/doc/ml_register_action.3.html
new file mode 100644 (file)
index 0000000..dddcbd3
--- /dev/null
@@ -0,0 +1,102 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ml_register_action</title>
+</head>
+<body>
+
+<h1 align=center>ml_register_action</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:03 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ml_register_action, ml_unregister_action - register callbacks</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;monolith.h&gt;
+
+const char *ml_register_action (ml_session session, void (*callback_fn) (ml_session, void *), void *data);
+void ml_unregister_action (ml_session session, const char *action_id);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Widgets such as buttons may register actions (callback
+functions) to be run when a user clicks a button or submits
+a form. Each action is associated with a unique action ID (a
+string). A separate set of actions is registered within each
+session. The callback function is invoked as <b>void
+callback_fn (ml_session session, void
+*data)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_register_action</b> registers an action within a given
+session and returns the action ID string.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_unregister_action</b> unregisters an
+action.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_button(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_session_args.3.html b/doc/ml_session_args.3.html
new file mode 100644 (file)
index 0000000..8673535
--- /dev/null
@@ -0,0 +1,131 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ml_session_pool</title>
+</head>
+<body>
+
+<h1 align=center>ml_session_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:04 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ml_session_pool, ml_session_args, ml_session_sessionid, ml_session_canonical_path, ml_session_script_name - get monolith per-session information</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;monolith.h&gt;
+
+pool ml_session_pool (ml_session);
+cgi ml_session_args (ml_session);
+const char *ml_session_sessionid (ml_session);
+const char *ml_session_canonical_path (ml_session);
+const char *ml_session_script_name (ml_session);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions extract the information from the opaque
+<b>ml_session</b> object passed to most application
+functions and callbacks. Each separate user session is tied
+to a separate <b>ml_session</b> object which contains
+standard fields.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_pool</b> returns the session pool, which is a
+pool which has the lifetime of the whole
+session.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_args</b> returns the arguments (a <b>cgi</b>
+object) which were passed to the application when it started
+running. This is kind of equivalent to <b>argc</b> and
+<b>argv</b> for a traditional application.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_sessionid</b> returns the session ID, a 32
+character string of random hex digits which also happens to
+be the cookie passed by the browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_canonical_path</b> returns the canonical path
+of the monolith application, that is, the full path of the
+application as it appears to the browser. For example
+<b>/so-bin/app.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_script_name</b> returns just the name of the
+application as it appears to the browser. For example
+<b>app.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_entry_point(3)</b>, <b>new_cgi(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_session_canonical_path.3.html b/doc/ml_session_canonical_path.3.html
new file mode 100644 (file)
index 0000000..8673535
--- /dev/null
@@ -0,0 +1,131 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ml_session_pool</title>
+</head>
+<body>
+
+<h1 align=center>ml_session_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:04 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ml_session_pool, ml_session_args, ml_session_sessionid, ml_session_canonical_path, ml_session_script_name - get monolith per-session information</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;monolith.h&gt;
+
+pool ml_session_pool (ml_session);
+cgi ml_session_args (ml_session);
+const char *ml_session_sessionid (ml_session);
+const char *ml_session_canonical_path (ml_session);
+const char *ml_session_script_name (ml_session);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions extract the information from the opaque
+<b>ml_session</b> object passed to most application
+functions and callbacks. Each separate user session is tied
+to a separate <b>ml_session</b> object which contains
+standard fields.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_pool</b> returns the session pool, which is a
+pool which has the lifetime of the whole
+session.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_args</b> returns the arguments (a <b>cgi</b>
+object) which were passed to the application when it started
+running. This is kind of equivalent to <b>argc</b> and
+<b>argv</b> for a traditional application.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_sessionid</b> returns the session ID, a 32
+character string of random hex digits which also happens to
+be the cookie passed by the browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_canonical_path</b> returns the canonical path
+of the monolith application, that is, the full path of the
+application as it appears to the browser. For example
+<b>/so-bin/app.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_script_name</b> returns just the name of the
+application as it appears to the browser. For example
+<b>app.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_entry_point(3)</b>, <b>new_cgi(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_session_pool.3.html b/doc/ml_session_pool.3.html
new file mode 100644 (file)
index 0000000..8673535
--- /dev/null
@@ -0,0 +1,131 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ml_session_pool</title>
+</head>
+<body>
+
+<h1 align=center>ml_session_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:04 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ml_session_pool, ml_session_args, ml_session_sessionid, ml_session_canonical_path, ml_session_script_name - get monolith per-session information</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;monolith.h&gt;
+
+pool ml_session_pool (ml_session);
+cgi ml_session_args (ml_session);
+const char *ml_session_sessionid (ml_session);
+const char *ml_session_canonical_path (ml_session);
+const char *ml_session_script_name (ml_session);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions extract the information from the opaque
+<b>ml_session</b> object passed to most application
+functions and callbacks. Each separate user session is tied
+to a separate <b>ml_session</b> object which contains
+standard fields.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_pool</b> returns the session pool, which is a
+pool which has the lifetime of the whole
+session.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_args</b> returns the arguments (a <b>cgi</b>
+object) which were passed to the application when it started
+running. This is kind of equivalent to <b>argc</b> and
+<b>argv</b> for a traditional application.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_sessionid</b> returns the session ID, a 32
+character string of random hex digits which also happens to
+be the cookie passed by the browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_canonical_path</b> returns the canonical path
+of the monolith application, that is, the full path of the
+application as it appears to the browser. For example
+<b>/so-bin/app.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_script_name</b> returns just the name of the
+application as it appears to the browser. For example
+<b>app.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_entry_point(3)</b>, <b>new_cgi(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_session_script_name.3.html b/doc/ml_session_script_name.3.html
new file mode 100644 (file)
index 0000000..8673535
--- /dev/null
@@ -0,0 +1,131 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ml_session_pool</title>
+</head>
+<body>
+
+<h1 align=center>ml_session_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:04 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ml_session_pool, ml_session_args, ml_session_sessionid, ml_session_canonical_path, ml_session_script_name - get monolith per-session information</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;monolith.h&gt;
+
+pool ml_session_pool (ml_session);
+cgi ml_session_args (ml_session);
+const char *ml_session_sessionid (ml_session);
+const char *ml_session_canonical_path (ml_session);
+const char *ml_session_script_name (ml_session);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions extract the information from the opaque
+<b>ml_session</b> object passed to most application
+functions and callbacks. Each separate user session is tied
+to a separate <b>ml_session</b> object which contains
+standard fields.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_pool</b> returns the session pool, which is a
+pool which has the lifetime of the whole
+session.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_args</b> returns the arguments (a <b>cgi</b>
+object) which were passed to the application when it started
+running. This is kind of equivalent to <b>argc</b> and
+<b>argv</b> for a traditional application.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_sessionid</b> returns the session ID, a 32
+character string of random hex digits which also happens to
+be the cookie passed by the browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_canonical_path</b> returns the canonical path
+of the monolith application, that is, the full path of the
+application as it appears to the browser. For example
+<b>/so-bin/app.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_script_name</b> returns just the name of the
+application as it appears to the browser. For example
+<b>app.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_entry_point(3)</b>, <b>new_cgi(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_session_sessionid.3.html b/doc/ml_session_sessionid.3.html
new file mode 100644 (file)
index 0000000..139b748
--- /dev/null
@@ -0,0 +1,131 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ml_session_pool</title>
+</head>
+<body>
+
+<h1 align=center>ml_session_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:05 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ml_session_pool, ml_session_args, ml_session_sessionid, ml_session_canonical_path, ml_session_script_name - get monolith per-session information</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;monolith.h&gt;
+
+pool ml_session_pool (ml_session);
+cgi ml_session_args (ml_session);
+const char *ml_session_sessionid (ml_session);
+const char *ml_session_canonical_path (ml_session);
+const char *ml_session_script_name (ml_session);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions extract the information from the opaque
+<b>ml_session</b> object passed to most application
+functions and callbacks. Each separate user session is tied
+to a separate <b>ml_session</b> object which contains
+standard fields.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_pool</b> returns the session pool, which is a
+pool which has the lifetime of the whole
+session.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_args</b> returns the arguments (a <b>cgi</b>
+object) which were passed to the application when it started
+running. This is kind of equivalent to <b>argc</b> and
+<b>argv</b> for a traditional application.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_sessionid</b> returns the session ID, a 32
+character string of random hex digits which also happens to
+be the cookie passed by the browser.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_canonical_path</b> returns the canonical path
+of the monolith application, that is, the full path of the
+application as it appears to the browser. For example
+<b>/so-bin/app.so</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_session_script_name</b> returns just the name of the
+application as it appears to the browser. For example
+<b>app.so</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_entry_point(3)</b>, <b>new_cgi(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_table_layout_pack.3.html b/doc/ml_table_layout_pack.3.html
new file mode 100644 (file)
index 0000000..63352a4
--- /dev/null
@@ -0,0 +1,142 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_table_layout</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_table_layout</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:05 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_table_layout, ml_table_layout_pack, ml_table_layout_set_colspan, ml_table_layout_set_rowspan, ml_table_layout_set_align, ml_table_layout_set_valign - monolith table layout widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_table_layout.h&gt;
+
+ml_table_layout new_ml_table_layout (pool, int rows, int cols);
+void ml_table_layout_pack (ml_table_layout, ml_widget, int row, int col);
+void ml_table_layout_set_colspan (ml_table_layout, int row, int col, int colspan);
+void ml_table_layout_set_rowspan (ml_table_layout, int row, int col, int rowspan);
+void ml_table_layout_set_align (ml_table_layout, int row, int col, const char *align);
+void ml_table_layout_set_valign (ml_table_layout, int row, int col, const char *valign);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The monolith table layout widget is a very powerful layout
+tool. It is modelled on, and indeed implemented using, HTML
+&lt;table&gt;s. Table layouts are grids of widgets with a
+fixed number of rows and columns. Each widget normally
+occupies a single cell of the table, but widgets may occupy
+a rectangle of several adjacent cells. All cells in the
+table are referenced using the row and column number, with
+row and column numbers starting from zero.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_table_layout</b> creates a new table layout widget
+with <b>rows</b> cells across and <b>cols</b> cells down.
+The cells are numbered <b>0 .. rows-1</b> across and code{0
+.. cols-1} down.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_pack</b> packs a widget at position
+<b>(row,col)</b> within the table. To remove a widget and
+leave a cell empty, pack a <b>NULL</b> widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_set_colspan</b> and
+<b>ml_table_layout_set_rowspan</b> set the column span and
+row span for a particular table cell respectively. The
+col/row-span allow a cell to occupy one or more adjacent
+cells in the table (any widgets packed in those adjacent
+cells are silently ignored). The default column and row span
+for every cell is 1.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_set_align</b> sets the horizontal
+alignment for the content of a cell. The possibilities are
+<b>&quot;left&quot;</b>, <b>&quot;center&quot;</b> or
+<b>&quot;right&quot;</b>, with the default being
+left-aligned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_set_valign</b> sets the vertical
+alignment for the content of a cell. The possibilities are
+<b>&quot;top&quot;</b>, <b>&quot;middle&quot;</b> or
+<b>&quot;bottom&quot;</b>, with the default being
+middle.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_widget(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_table_layout_set_align.3.html b/doc/ml_table_layout_set_align.3.html
new file mode 100644 (file)
index 0000000..63352a4
--- /dev/null
@@ -0,0 +1,142 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_table_layout</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_table_layout</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:05 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_table_layout, ml_table_layout_pack, ml_table_layout_set_colspan, ml_table_layout_set_rowspan, ml_table_layout_set_align, ml_table_layout_set_valign - monolith table layout widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_table_layout.h&gt;
+
+ml_table_layout new_ml_table_layout (pool, int rows, int cols);
+void ml_table_layout_pack (ml_table_layout, ml_widget, int row, int col);
+void ml_table_layout_set_colspan (ml_table_layout, int row, int col, int colspan);
+void ml_table_layout_set_rowspan (ml_table_layout, int row, int col, int rowspan);
+void ml_table_layout_set_align (ml_table_layout, int row, int col, const char *align);
+void ml_table_layout_set_valign (ml_table_layout, int row, int col, const char *valign);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The monolith table layout widget is a very powerful layout
+tool. It is modelled on, and indeed implemented using, HTML
+&lt;table&gt;s. Table layouts are grids of widgets with a
+fixed number of rows and columns. Each widget normally
+occupies a single cell of the table, but widgets may occupy
+a rectangle of several adjacent cells. All cells in the
+table are referenced using the row and column number, with
+row and column numbers starting from zero.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_table_layout</b> creates a new table layout widget
+with <b>rows</b> cells across and <b>cols</b> cells down.
+The cells are numbered <b>0 .. rows-1</b> across and code{0
+.. cols-1} down.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_pack</b> packs a widget at position
+<b>(row,col)</b> within the table. To remove a widget and
+leave a cell empty, pack a <b>NULL</b> widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_set_colspan</b> and
+<b>ml_table_layout_set_rowspan</b> set the column span and
+row span for a particular table cell respectively. The
+col/row-span allow a cell to occupy one or more adjacent
+cells in the table (any widgets packed in those adjacent
+cells are silently ignored). The default column and row span
+for every cell is 1.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_set_align</b> sets the horizontal
+alignment for the content of a cell. The possibilities are
+<b>&quot;left&quot;</b>, <b>&quot;center&quot;</b> or
+<b>&quot;right&quot;</b>, with the default being
+left-aligned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_set_valign</b> sets the vertical
+alignment for the content of a cell. The possibilities are
+<b>&quot;top&quot;</b>, <b>&quot;middle&quot;</b> or
+<b>&quot;bottom&quot;</b>, with the default being
+middle.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_widget(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_table_layout_set_colspan.3.html b/doc/ml_table_layout_set_colspan.3.html
new file mode 100644 (file)
index 0000000..63352a4
--- /dev/null
@@ -0,0 +1,142 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_table_layout</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_table_layout</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:05 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_table_layout, ml_table_layout_pack, ml_table_layout_set_colspan, ml_table_layout_set_rowspan, ml_table_layout_set_align, ml_table_layout_set_valign - monolith table layout widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_table_layout.h&gt;
+
+ml_table_layout new_ml_table_layout (pool, int rows, int cols);
+void ml_table_layout_pack (ml_table_layout, ml_widget, int row, int col);
+void ml_table_layout_set_colspan (ml_table_layout, int row, int col, int colspan);
+void ml_table_layout_set_rowspan (ml_table_layout, int row, int col, int rowspan);
+void ml_table_layout_set_align (ml_table_layout, int row, int col, const char *align);
+void ml_table_layout_set_valign (ml_table_layout, int row, int col, const char *valign);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The monolith table layout widget is a very powerful layout
+tool. It is modelled on, and indeed implemented using, HTML
+&lt;table&gt;s. Table layouts are grids of widgets with a
+fixed number of rows and columns. Each widget normally
+occupies a single cell of the table, but widgets may occupy
+a rectangle of several adjacent cells. All cells in the
+table are referenced using the row and column number, with
+row and column numbers starting from zero.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_table_layout</b> creates a new table layout widget
+with <b>rows</b> cells across and <b>cols</b> cells down.
+The cells are numbered <b>0 .. rows-1</b> across and code{0
+.. cols-1} down.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_pack</b> packs a widget at position
+<b>(row,col)</b> within the table. To remove a widget and
+leave a cell empty, pack a <b>NULL</b> widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_set_colspan</b> and
+<b>ml_table_layout_set_rowspan</b> set the column span and
+row span for a particular table cell respectively. The
+col/row-span allow a cell to occupy one or more adjacent
+cells in the table (any widgets packed in those adjacent
+cells are silently ignored). The default column and row span
+for every cell is 1.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_set_align</b> sets the horizontal
+alignment for the content of a cell. The possibilities are
+<b>&quot;left&quot;</b>, <b>&quot;center&quot;</b> or
+<b>&quot;right&quot;</b>, with the default being
+left-aligned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_set_valign</b> sets the vertical
+alignment for the content of a cell. The possibilities are
+<b>&quot;top&quot;</b>, <b>&quot;middle&quot;</b> or
+<b>&quot;bottom&quot;</b>, with the default being
+middle.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_widget(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_table_layout_set_rowspan.3.html b/doc/ml_table_layout_set_rowspan.3.html
new file mode 100644 (file)
index 0000000..205a840
--- /dev/null
@@ -0,0 +1,142 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_table_layout</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_table_layout</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:06 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_table_layout, ml_table_layout_pack, ml_table_layout_set_colspan, ml_table_layout_set_rowspan, ml_table_layout_set_align, ml_table_layout_set_valign - monolith table layout widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_table_layout.h&gt;
+
+ml_table_layout new_ml_table_layout (pool, int rows, int cols);
+void ml_table_layout_pack (ml_table_layout, ml_widget, int row, int col);
+void ml_table_layout_set_colspan (ml_table_layout, int row, int col, int colspan);
+void ml_table_layout_set_rowspan (ml_table_layout, int row, int col, int rowspan);
+void ml_table_layout_set_align (ml_table_layout, int row, int col, const char *align);
+void ml_table_layout_set_valign (ml_table_layout, int row, int col, const char *valign);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The monolith table layout widget is a very powerful layout
+tool. It is modelled on, and indeed implemented using, HTML
+&lt;table&gt;s. Table layouts are grids of widgets with a
+fixed number of rows and columns. Each widget normally
+occupies a single cell of the table, but widgets may occupy
+a rectangle of several adjacent cells. All cells in the
+table are referenced using the row and column number, with
+row and column numbers starting from zero.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_table_layout</b> creates a new table layout widget
+with <b>rows</b> cells across and <b>cols</b> cells down.
+The cells are numbered <b>0 .. rows-1</b> across and code{0
+.. cols-1} down.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_pack</b> packs a widget at position
+<b>(row,col)</b> within the table. To remove a widget and
+leave a cell empty, pack a <b>NULL</b> widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_set_colspan</b> and
+<b>ml_table_layout_set_rowspan</b> set the column span and
+row span for a particular table cell respectively. The
+col/row-span allow a cell to occupy one or more adjacent
+cells in the table (any widgets packed in those adjacent
+cells are silently ignored). The default column and row span
+for every cell is 1.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_set_align</b> sets the horizontal
+alignment for the content of a cell. The possibilities are
+<b>&quot;left&quot;</b>, <b>&quot;center&quot;</b> or
+<b>&quot;right&quot;</b>, with the default being
+left-aligned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_set_valign</b> sets the vertical
+alignment for the content of a cell. The possibilities are
+<b>&quot;top&quot;</b>, <b>&quot;middle&quot;</b> or
+<b>&quot;bottom&quot;</b>, with the default being
+middle.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_widget(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_table_layout_set_valign.3.html b/doc/ml_table_layout_set_valign.3.html
new file mode 100644 (file)
index 0000000..205a840
--- /dev/null
@@ -0,0 +1,142 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_table_layout</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_table_layout</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:06 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_table_layout, ml_table_layout_pack, ml_table_layout_set_colspan, ml_table_layout_set_rowspan, ml_table_layout_set_align, ml_table_layout_set_valign - monolith table layout widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_table_layout.h&gt;
+
+ml_table_layout new_ml_table_layout (pool, int rows, int cols);
+void ml_table_layout_pack (ml_table_layout, ml_widget, int row, int col);
+void ml_table_layout_set_colspan (ml_table_layout, int row, int col, int colspan);
+void ml_table_layout_set_rowspan (ml_table_layout, int row, int col, int rowspan);
+void ml_table_layout_set_align (ml_table_layout, int row, int col, const char *align);
+void ml_table_layout_set_valign (ml_table_layout, int row, int col, const char *valign);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The monolith table layout widget is a very powerful layout
+tool. It is modelled on, and indeed implemented using, HTML
+&lt;table&gt;s. Table layouts are grids of widgets with a
+fixed number of rows and columns. Each widget normally
+occupies a single cell of the table, but widgets may occupy
+a rectangle of several adjacent cells. All cells in the
+table are referenced using the row and column number, with
+row and column numbers starting from zero.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_table_layout</b> creates a new table layout widget
+with <b>rows</b> cells across and <b>cols</b> cells down.
+The cells are numbered <b>0 .. rows-1</b> across and code{0
+.. cols-1} down.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_pack</b> packs a widget at position
+<b>(row,col)</b> within the table. To remove a widget and
+leave a cell empty, pack a <b>NULL</b> widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_set_colspan</b> and
+<b>ml_table_layout_set_rowspan</b> set the column span and
+row span for a particular table cell respectively. The
+col/row-span allow a cell to occupy one or more adjacent
+cells in the table (any widgets packed in those adjacent
+cells are silently ignored). The default column and row span
+for every cell is 1.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_set_align</b> sets the horizontal
+alignment for the content of a cell. The possibilities are
+<b>&quot;left&quot;</b>, <b>&quot;center&quot;</b> or
+<b>&quot;right&quot;</b>, with the default being
+left-aligned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_set_valign</b> sets the vertical
+alignment for the content of a cell. The possibilities are
+<b>&quot;top&quot;</b>, <b>&quot;middle&quot;</b> or
+<b>&quot;bottom&quot;</b>, with the default being
+middle.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_widget(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_text_label_set_font_size.3.html b/doc/ml_text_label_set_font_size.3.html
new file mode 100644 (file)
index 0000000..ab377ec
--- /dev/null
@@ -0,0 +1,138 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_text_label</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_text_label</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:06 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_text_label, ml_text_label_set_text_align, ml_text_label_set_font_weight, ml_text_label_set_font_size, ml_text_label_set_text - monolith text label widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_text_label.h&gt;
+
+ml_text_label new_ml_text_label (pool, const char *text);
+void ml_text_label_set_text_align (ml_text_label, const char *);
+void ml_text_label_set_font_weight (ml_text_label, const char *);
+void ml_text_label_set_font_size (ml_text_label, const char *);
+void ml_text_label_set_text (ml_text_label, const char *);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Text labels are simple strings or paragraphs of text. Unlike
+the <b>ml_label</b> widget, HTML sequences are escaped
+before being sent to the browser, so <b>ml_text_label</b>s
+can contain <b>&lt;</b>, <b>&gt;</b>, <b>&amp;</b> and so on
+and these will be displayed correctly.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Text labels allow control over the style, size and other
+aspects of how the text appears.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_text_label</b> creates a new text label
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_set_text_align</b> sets the text-align
+style of the text label. Common alignments are
+<b>&quot;left&quot;</b>, <b>&quot;right&quot;</b> and
+<b>&quot;justify&quot;</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_set_font_weight</b> sets the font-weight
+style of the text label. Common weights are
+<b>&quot;normal&quot;</b> and
+<b>&quot;bold&quot;</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_set_font_size</b> sets the font-size style
+of the text label. Common sizes are
+<b>&quot;small&quot;</b>, <b>&quot;medium&quot;</b> and
+<b>&quot;large&quot;</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_(set|get)_text</b> updates the text
+displayed on the label.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_widget(3)</b>, W3C CSS2
+recommendation.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_text_label_set_font_weight.3.html b/doc/ml_text_label_set_font_weight.3.html
new file mode 100644 (file)
index 0000000..ab377ec
--- /dev/null
@@ -0,0 +1,138 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_text_label</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_text_label</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:06 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_text_label, ml_text_label_set_text_align, ml_text_label_set_font_weight, ml_text_label_set_font_size, ml_text_label_set_text - monolith text label widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_text_label.h&gt;
+
+ml_text_label new_ml_text_label (pool, const char *text);
+void ml_text_label_set_text_align (ml_text_label, const char *);
+void ml_text_label_set_font_weight (ml_text_label, const char *);
+void ml_text_label_set_font_size (ml_text_label, const char *);
+void ml_text_label_set_text (ml_text_label, const char *);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Text labels are simple strings or paragraphs of text. Unlike
+the <b>ml_label</b> widget, HTML sequences are escaped
+before being sent to the browser, so <b>ml_text_label</b>s
+can contain <b>&lt;</b>, <b>&gt;</b>, <b>&amp;</b> and so on
+and these will be displayed correctly.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Text labels allow control over the style, size and other
+aspects of how the text appears.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_text_label</b> creates a new text label
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_set_text_align</b> sets the text-align
+style of the text label. Common alignments are
+<b>&quot;left&quot;</b>, <b>&quot;right&quot;</b> and
+<b>&quot;justify&quot;</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_set_font_weight</b> sets the font-weight
+style of the text label. Common weights are
+<b>&quot;normal&quot;</b> and
+<b>&quot;bold&quot;</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_set_font_size</b> sets the font-size style
+of the text label. Common sizes are
+<b>&quot;small&quot;</b>, <b>&quot;medium&quot;</b> and
+<b>&quot;large&quot;</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_(set|get)_text</b> updates the text
+displayed on the label.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_widget(3)</b>, W3C CSS2
+recommendation.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_text_label_set_text.3.html b/doc/ml_text_label_set_text.3.html
new file mode 100644 (file)
index 0000000..ab377ec
--- /dev/null
@@ -0,0 +1,138 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_text_label</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_text_label</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:06 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_text_label, ml_text_label_set_text_align, ml_text_label_set_font_weight, ml_text_label_set_font_size, ml_text_label_set_text - monolith text label widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_text_label.h&gt;
+
+ml_text_label new_ml_text_label (pool, const char *text);
+void ml_text_label_set_text_align (ml_text_label, const char *);
+void ml_text_label_set_font_weight (ml_text_label, const char *);
+void ml_text_label_set_font_size (ml_text_label, const char *);
+void ml_text_label_set_text (ml_text_label, const char *);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Text labels are simple strings or paragraphs of text. Unlike
+the <b>ml_label</b> widget, HTML sequences are escaped
+before being sent to the browser, so <b>ml_text_label</b>s
+can contain <b>&lt;</b>, <b>&gt;</b>, <b>&amp;</b> and so on
+and these will be displayed correctly.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Text labels allow control over the style, size and other
+aspects of how the text appears.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_text_label</b> creates a new text label
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_set_text_align</b> sets the text-align
+style of the text label. Common alignments are
+<b>&quot;left&quot;</b>, <b>&quot;right&quot;</b> and
+<b>&quot;justify&quot;</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_set_font_weight</b> sets the font-weight
+style of the text label. Common weights are
+<b>&quot;normal&quot;</b> and
+<b>&quot;bold&quot;</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_set_font_size</b> sets the font-size style
+of the text label. Common sizes are
+<b>&quot;small&quot;</b>, <b>&quot;medium&quot;</b> and
+<b>&quot;large&quot;</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_(set|get)_text</b> updates the text
+displayed on the label.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_widget(3)</b>, W3C CSS2
+recommendation.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_text_label_set_text_align.3.html b/doc/ml_text_label_set_text_align.3.html
new file mode 100644 (file)
index 0000000..308f343
--- /dev/null
@@ -0,0 +1,138 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_text_label</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_text_label</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:07 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_text_label, ml_text_label_set_text_align, ml_text_label_set_font_weight, ml_text_label_set_font_size, ml_text_label_set_text - monolith text label widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_text_label.h&gt;
+
+ml_text_label new_ml_text_label (pool, const char *text);
+void ml_text_label_set_text_align (ml_text_label, const char *);
+void ml_text_label_set_font_weight (ml_text_label, const char *);
+void ml_text_label_set_font_size (ml_text_label, const char *);
+void ml_text_label_set_text (ml_text_label, const char *);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Text labels are simple strings or paragraphs of text. Unlike
+the <b>ml_label</b> widget, HTML sequences are escaped
+before being sent to the browser, so <b>ml_text_label</b>s
+can contain <b>&lt;</b>, <b>&gt;</b>, <b>&amp;</b> and so on
+and these will be displayed correctly.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Text labels allow control over the style, size and other
+aspects of how the text appears.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_text_label</b> creates a new text label
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_set_text_align</b> sets the text-align
+style of the text label. Common alignments are
+<b>&quot;left&quot;</b>, <b>&quot;right&quot;</b> and
+<b>&quot;justify&quot;</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_set_font_weight</b> sets the font-weight
+style of the text label. Common weights are
+<b>&quot;normal&quot;</b> and
+<b>&quot;bold&quot;</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_set_font_size</b> sets the font-size style
+of the text label. Common sizes are
+<b>&quot;small&quot;</b>, <b>&quot;medium&quot;</b> and
+<b>&quot;large&quot;</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_(set|get)_text</b> updates the text
+displayed on the label.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_widget(3)</b>, W3C CSS2
+recommendation.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_unregister_action.3.html b/doc/ml_unregister_action.3.html
new file mode 100644 (file)
index 0000000..522b491
--- /dev/null
@@ -0,0 +1,102 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ml_register_action</title>
+</head>
+<body>
+
+<h1 align=center>ml_register_action</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:07 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ml_register_action, ml_unregister_action - register callbacks</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;monolith.h&gt;
+
+const char *ml_register_action (ml_session session, void (*callback_fn) (ml_session, void *), void *data);
+void ml_unregister_action (ml_session session, const char *action_id);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Widgets such as buttons may register actions (callback
+functions) to be run when a user clicks a button or submits
+a form. Each action is associated with a unique action ID (a
+string). A separate set of actions is registered within each
+session. The callback function is invoked as <b>void
+callback_fn (ml_session session, void
+*data)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_register_action</b> registers an action within a given
+session and returns the action ID string.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_unregister_action</b> unregisters an
+action.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_button(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_widget_repaint.3.html b/doc/ml_widget_repaint.3.html
new file mode 100644 (file)
index 0000000..e018a87
--- /dev/null
@@ -0,0 +1,73 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ml_widget_repaint</title>
+</head>
+<body>
+
+<h1 align=center>ml_widget_repaint</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:07 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ml_widget_repaint - Operations on generic monolith widgets.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_widget.h&gt;
+
+void ml_widget_repaint (ml_widget widget, struct ml_session *, const char *windowid, io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_widget_repaint</b> calls the repaint function on a
+generic widget.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_window_get_charset.3.html b/doc/ml_window_get_charset.3.html
new file mode 100644 (file)
index 0000000..03673a4
--- /dev/null
@@ -0,0 +1,205 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_window</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_window</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:07 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_window, ml_window_pack, ml_window_set_title, ml_window_get_title, ml_window_set_stylesheet, ml_window_get_stylesheet, ml_window_set_charset, ml_window_get_charset, new_ml_frameset, ml_frameset_set_description, ml_frameset_set_title, ml_frameset_get_title - monolith window and frameset</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_window.h&gt;
+
+ml_window new_ml_window (struct ml_session *, pool pool);
+void ml_window_pack (ml_window, ml_widget);
+void ml_window_set_title (ml_window, const char *title);
+const char *ml_window_get_title (ml_window);
+void ml_window_set_stylesheet (ml_window, const char *stylesheet);
+const char *ml_window_get_stylesheet (ml_window);
+void ml_window_set_charset (ml_window, const char *charset);
+const char *ml_window_get_charset (ml_window);
+ml_window new_ml_frameset (struct ml_session *, pool pool, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_description (ml_window, struct ml_session *, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_title (ml_window, const char *);
+const char *ml_frameset_get_title (ml_window);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;window&quot; is a top-level window. Every
+application has at least one window, created in the main
+function. A window is just a wrapper. To actually do
+anything, you must pack a single widget inside the window.
+Windows can only take a single widget. If you want more than
+one widget to appear inside a window, then you must pack
+them into some sort of layout widget first, and pack the
+layout widget into the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;frameset&quot; is used to create framesets (several
+ordinary windows packed into one top-level window, with
+independent scrolling capabilities). Windows and framesets
+are actually so similar, that I have included them in the
+same class (and also so that other code can deal with an
+opaque <b>ml_window</b> pointer and not have to worry about
+whether it is a window or a frameset). Framesets may contain
+windows or other framesets, or a mixture of
+both.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Monolith windows are not widgets (unlike many of the other
+classes in monolith).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_window</b> creates a new monolith
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_pack</b> packs a widget into the window. Since
+a window can only contain a single widget, subsequent calls
+to this function overwrite the packed widget. (Note: this
+call does not apply to framesets).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_title</b> changes the title of the
+window. The title of the window defaults to no title at all,
+so it is a good idea to set this.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_stylesheet</b> changes the stylesheet
+of the page. Monolith default stylesheets are installed in
+<b>/ml-styles/</b>, and the default stylesheet is
+<b>/ml-styles/default.css</b> (supplied in the monolith
+distribution). Stylesheets are used to 'theme' monolith
+applications.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_charset</b> changes the character
+encoding associated with the window. Because of limitations
+in HTML, only a single charset can be associated with all of
+the widgets in a window. The default charset is
+<b>iso-8859-1</b>. For multilingual support, it is
+recommended that users change the charset to either their
+native language encoding, or to <b>utf-8</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_frameset</b> creates a new frameset. <b>rows</b>
+and <b>cols</b> define the number of frames and their
+layout. You can use <b>rows</b> and <b>cols</b> to create
+frameset layouts including grids, as described in the HTML 4
+standard (<b>http://www.w3.org/TR/html401/</b>). To create
+nested framesets, use a frame which generates a frameset
+instead of a window. <b>frames</b> is a vector of <b>struct
+ml_frame_description</b> structures (note: the structures
+themselves, not pointers to the structures). <b>struct
+ml_frame_description</b> contains the following
+fields:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fn</b>: The function which is called generate the frame
+(or nested frameset). This function must call either
+<b>new_ml_window</b> or <b>new_ml_frameset</b>. The function
+is prototyped as <b>void fn (ml_session session, void
+*data);</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>data</b>: Data pointer passed to this
+function.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_set_description</b> allows the frameset
+description to be updated.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_(set|get)_title</b> updates the window
+title.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_window_get_stylesheet.3.html b/doc/ml_window_get_stylesheet.3.html
new file mode 100644 (file)
index 0000000..a8f30b4
--- /dev/null
@@ -0,0 +1,205 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_window</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_window</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:08 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_window, ml_window_pack, ml_window_set_title, ml_window_get_title, ml_window_set_stylesheet, ml_window_get_stylesheet, ml_window_set_charset, ml_window_get_charset, new_ml_frameset, ml_frameset_set_description, ml_frameset_set_title, ml_frameset_get_title - monolith window and frameset</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_window.h&gt;
+
+ml_window new_ml_window (struct ml_session *, pool pool);
+void ml_window_pack (ml_window, ml_widget);
+void ml_window_set_title (ml_window, const char *title);
+const char *ml_window_get_title (ml_window);
+void ml_window_set_stylesheet (ml_window, const char *stylesheet);
+const char *ml_window_get_stylesheet (ml_window);
+void ml_window_set_charset (ml_window, const char *charset);
+const char *ml_window_get_charset (ml_window);
+ml_window new_ml_frameset (struct ml_session *, pool pool, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_description (ml_window, struct ml_session *, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_title (ml_window, const char *);
+const char *ml_frameset_get_title (ml_window);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;window&quot; is a top-level window. Every
+application has at least one window, created in the main
+function. A window is just a wrapper. To actually do
+anything, you must pack a single widget inside the window.
+Windows can only take a single widget. If you want more than
+one widget to appear inside a window, then you must pack
+them into some sort of layout widget first, and pack the
+layout widget into the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;frameset&quot; is used to create framesets (several
+ordinary windows packed into one top-level window, with
+independent scrolling capabilities). Windows and framesets
+are actually so similar, that I have included them in the
+same class (and also so that other code can deal with an
+opaque <b>ml_window</b> pointer and not have to worry about
+whether it is a window or a frameset). Framesets may contain
+windows or other framesets, or a mixture of
+both.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Monolith windows are not widgets (unlike many of the other
+classes in monolith).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_window</b> creates a new monolith
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_pack</b> packs a widget into the window. Since
+a window can only contain a single widget, subsequent calls
+to this function overwrite the packed widget. (Note: this
+call does not apply to framesets).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_title</b> changes the title of the
+window. The title of the window defaults to no title at all,
+so it is a good idea to set this.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_stylesheet</b> changes the stylesheet
+of the page. Monolith default stylesheets are installed in
+<b>/ml-styles/</b>, and the default stylesheet is
+<b>/ml-styles/default.css</b> (supplied in the monolith
+distribution). Stylesheets are used to 'theme' monolith
+applications.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_charset</b> changes the character
+encoding associated with the window. Because of limitations
+in HTML, only a single charset can be associated with all of
+the widgets in a window. The default charset is
+<b>iso-8859-1</b>. For multilingual support, it is
+recommended that users change the charset to either their
+native language encoding, or to <b>utf-8</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_frameset</b> creates a new frameset. <b>rows</b>
+and <b>cols</b> define the number of frames and their
+layout. You can use <b>rows</b> and <b>cols</b> to create
+frameset layouts including grids, as described in the HTML 4
+standard (<b>http://www.w3.org/TR/html401/</b>). To create
+nested framesets, use a frame which generates a frameset
+instead of a window. <b>frames</b> is a vector of <b>struct
+ml_frame_description</b> structures (note: the structures
+themselves, not pointers to the structures). <b>struct
+ml_frame_description</b> contains the following
+fields:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fn</b>: The function which is called generate the frame
+(or nested frameset). This function must call either
+<b>new_ml_window</b> or <b>new_ml_frameset</b>. The function
+is prototyped as <b>void fn (ml_session session, void
+*data);</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>data</b>: Data pointer passed to this
+function.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_set_description</b> allows the frameset
+description to be updated.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_(set|get)_title</b> updates the window
+title.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_window_get_title.3.html b/doc/ml_window_get_title.3.html
new file mode 100644 (file)
index 0000000..a8f30b4
--- /dev/null
@@ -0,0 +1,205 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_window</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_window</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:08 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_window, ml_window_pack, ml_window_set_title, ml_window_get_title, ml_window_set_stylesheet, ml_window_get_stylesheet, ml_window_set_charset, ml_window_get_charset, new_ml_frameset, ml_frameset_set_description, ml_frameset_set_title, ml_frameset_get_title - monolith window and frameset</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_window.h&gt;
+
+ml_window new_ml_window (struct ml_session *, pool pool);
+void ml_window_pack (ml_window, ml_widget);
+void ml_window_set_title (ml_window, const char *title);
+const char *ml_window_get_title (ml_window);
+void ml_window_set_stylesheet (ml_window, const char *stylesheet);
+const char *ml_window_get_stylesheet (ml_window);
+void ml_window_set_charset (ml_window, const char *charset);
+const char *ml_window_get_charset (ml_window);
+ml_window new_ml_frameset (struct ml_session *, pool pool, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_description (ml_window, struct ml_session *, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_title (ml_window, const char *);
+const char *ml_frameset_get_title (ml_window);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;window&quot; is a top-level window. Every
+application has at least one window, created in the main
+function. A window is just a wrapper. To actually do
+anything, you must pack a single widget inside the window.
+Windows can only take a single widget. If you want more than
+one widget to appear inside a window, then you must pack
+them into some sort of layout widget first, and pack the
+layout widget into the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;frameset&quot; is used to create framesets (several
+ordinary windows packed into one top-level window, with
+independent scrolling capabilities). Windows and framesets
+are actually so similar, that I have included them in the
+same class (and also so that other code can deal with an
+opaque <b>ml_window</b> pointer and not have to worry about
+whether it is a window or a frameset). Framesets may contain
+windows or other framesets, or a mixture of
+both.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Monolith windows are not widgets (unlike many of the other
+classes in monolith).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_window</b> creates a new monolith
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_pack</b> packs a widget into the window. Since
+a window can only contain a single widget, subsequent calls
+to this function overwrite the packed widget. (Note: this
+call does not apply to framesets).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_title</b> changes the title of the
+window. The title of the window defaults to no title at all,
+so it is a good idea to set this.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_stylesheet</b> changes the stylesheet
+of the page. Monolith default stylesheets are installed in
+<b>/ml-styles/</b>, and the default stylesheet is
+<b>/ml-styles/default.css</b> (supplied in the monolith
+distribution). Stylesheets are used to 'theme' monolith
+applications.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_charset</b> changes the character
+encoding associated with the window. Because of limitations
+in HTML, only a single charset can be associated with all of
+the widgets in a window. The default charset is
+<b>iso-8859-1</b>. For multilingual support, it is
+recommended that users change the charset to either their
+native language encoding, or to <b>utf-8</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_frameset</b> creates a new frameset. <b>rows</b>
+and <b>cols</b> define the number of frames and their
+layout. You can use <b>rows</b> and <b>cols</b> to create
+frameset layouts including grids, as described in the HTML 4
+standard (<b>http://www.w3.org/TR/html401/</b>). To create
+nested framesets, use a frame which generates a frameset
+instead of a window. <b>frames</b> is a vector of <b>struct
+ml_frame_description</b> structures (note: the structures
+themselves, not pointers to the structures). <b>struct
+ml_frame_description</b> contains the following
+fields:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fn</b>: The function which is called generate the frame
+(or nested frameset). This function must call either
+<b>new_ml_window</b> or <b>new_ml_frameset</b>. The function
+is prototyped as <b>void fn (ml_session session, void
+*data);</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>data</b>: Data pointer passed to this
+function.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_set_description</b> allows the frameset
+description to be updated.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_(set|get)_title</b> updates the window
+title.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_window_pack.3.html b/doc/ml_window_pack.3.html
new file mode 100644 (file)
index 0000000..a8f30b4
--- /dev/null
@@ -0,0 +1,205 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_window</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_window</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:08 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_window, ml_window_pack, ml_window_set_title, ml_window_get_title, ml_window_set_stylesheet, ml_window_get_stylesheet, ml_window_set_charset, ml_window_get_charset, new_ml_frameset, ml_frameset_set_description, ml_frameset_set_title, ml_frameset_get_title - monolith window and frameset</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_window.h&gt;
+
+ml_window new_ml_window (struct ml_session *, pool pool);
+void ml_window_pack (ml_window, ml_widget);
+void ml_window_set_title (ml_window, const char *title);
+const char *ml_window_get_title (ml_window);
+void ml_window_set_stylesheet (ml_window, const char *stylesheet);
+const char *ml_window_get_stylesheet (ml_window);
+void ml_window_set_charset (ml_window, const char *charset);
+const char *ml_window_get_charset (ml_window);
+ml_window new_ml_frameset (struct ml_session *, pool pool, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_description (ml_window, struct ml_session *, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_title (ml_window, const char *);
+const char *ml_frameset_get_title (ml_window);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;window&quot; is a top-level window. Every
+application has at least one window, created in the main
+function. A window is just a wrapper. To actually do
+anything, you must pack a single widget inside the window.
+Windows can only take a single widget. If you want more than
+one widget to appear inside a window, then you must pack
+them into some sort of layout widget first, and pack the
+layout widget into the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;frameset&quot; is used to create framesets (several
+ordinary windows packed into one top-level window, with
+independent scrolling capabilities). Windows and framesets
+are actually so similar, that I have included them in the
+same class (and also so that other code can deal with an
+opaque <b>ml_window</b> pointer and not have to worry about
+whether it is a window or a frameset). Framesets may contain
+windows or other framesets, or a mixture of
+both.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Monolith windows are not widgets (unlike many of the other
+classes in monolith).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_window</b> creates a new monolith
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_pack</b> packs a widget into the window. Since
+a window can only contain a single widget, subsequent calls
+to this function overwrite the packed widget. (Note: this
+call does not apply to framesets).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_title</b> changes the title of the
+window. The title of the window defaults to no title at all,
+so it is a good idea to set this.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_stylesheet</b> changes the stylesheet
+of the page. Monolith default stylesheets are installed in
+<b>/ml-styles/</b>, and the default stylesheet is
+<b>/ml-styles/default.css</b> (supplied in the monolith
+distribution). Stylesheets are used to 'theme' monolith
+applications.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_charset</b> changes the character
+encoding associated with the window. Because of limitations
+in HTML, only a single charset can be associated with all of
+the widgets in a window. The default charset is
+<b>iso-8859-1</b>. For multilingual support, it is
+recommended that users change the charset to either their
+native language encoding, or to <b>utf-8</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_frameset</b> creates a new frameset. <b>rows</b>
+and <b>cols</b> define the number of frames and their
+layout. You can use <b>rows</b> and <b>cols</b> to create
+frameset layouts including grids, as described in the HTML 4
+standard (<b>http://www.w3.org/TR/html401/</b>). To create
+nested framesets, use a frame which generates a frameset
+instead of a window. <b>frames</b> is a vector of <b>struct
+ml_frame_description</b> structures (note: the structures
+themselves, not pointers to the structures). <b>struct
+ml_frame_description</b> contains the following
+fields:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fn</b>: The function which is called generate the frame
+(or nested frameset). This function must call either
+<b>new_ml_window</b> or <b>new_ml_frameset</b>. The function
+is prototyped as <b>void fn (ml_session session, void
+*data);</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>data</b>: Data pointer passed to this
+function.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_set_description</b> allows the frameset
+description to be updated.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_(set|get)_title</b> updates the window
+title.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_window_set_charset.3.html b/doc/ml_window_set_charset.3.html
new file mode 100644 (file)
index 0000000..a8f30b4
--- /dev/null
@@ -0,0 +1,205 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_window</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_window</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:08 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_window, ml_window_pack, ml_window_set_title, ml_window_get_title, ml_window_set_stylesheet, ml_window_get_stylesheet, ml_window_set_charset, ml_window_get_charset, new_ml_frameset, ml_frameset_set_description, ml_frameset_set_title, ml_frameset_get_title - monolith window and frameset</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_window.h&gt;
+
+ml_window new_ml_window (struct ml_session *, pool pool);
+void ml_window_pack (ml_window, ml_widget);
+void ml_window_set_title (ml_window, const char *title);
+const char *ml_window_get_title (ml_window);
+void ml_window_set_stylesheet (ml_window, const char *stylesheet);
+const char *ml_window_get_stylesheet (ml_window);
+void ml_window_set_charset (ml_window, const char *charset);
+const char *ml_window_get_charset (ml_window);
+ml_window new_ml_frameset (struct ml_session *, pool pool, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_description (ml_window, struct ml_session *, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_title (ml_window, const char *);
+const char *ml_frameset_get_title (ml_window);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;window&quot; is a top-level window. Every
+application has at least one window, created in the main
+function. A window is just a wrapper. To actually do
+anything, you must pack a single widget inside the window.
+Windows can only take a single widget. If you want more than
+one widget to appear inside a window, then you must pack
+them into some sort of layout widget first, and pack the
+layout widget into the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;frameset&quot; is used to create framesets (several
+ordinary windows packed into one top-level window, with
+independent scrolling capabilities). Windows and framesets
+are actually so similar, that I have included them in the
+same class (and also so that other code can deal with an
+opaque <b>ml_window</b> pointer and not have to worry about
+whether it is a window or a frameset). Framesets may contain
+windows or other framesets, or a mixture of
+both.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Monolith windows are not widgets (unlike many of the other
+classes in monolith).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_window</b> creates a new monolith
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_pack</b> packs a widget into the window. Since
+a window can only contain a single widget, subsequent calls
+to this function overwrite the packed widget. (Note: this
+call does not apply to framesets).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_title</b> changes the title of the
+window. The title of the window defaults to no title at all,
+so it is a good idea to set this.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_stylesheet</b> changes the stylesheet
+of the page. Monolith default stylesheets are installed in
+<b>/ml-styles/</b>, and the default stylesheet is
+<b>/ml-styles/default.css</b> (supplied in the monolith
+distribution). Stylesheets are used to 'theme' monolith
+applications.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_charset</b> changes the character
+encoding associated with the window. Because of limitations
+in HTML, only a single charset can be associated with all of
+the widgets in a window. The default charset is
+<b>iso-8859-1</b>. For multilingual support, it is
+recommended that users change the charset to either their
+native language encoding, or to <b>utf-8</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_frameset</b> creates a new frameset. <b>rows</b>
+and <b>cols</b> define the number of frames and their
+layout. You can use <b>rows</b> and <b>cols</b> to create
+frameset layouts including grids, as described in the HTML 4
+standard (<b>http://www.w3.org/TR/html401/</b>). To create
+nested framesets, use a frame which generates a frameset
+instead of a window. <b>frames</b> is a vector of <b>struct
+ml_frame_description</b> structures (note: the structures
+themselves, not pointers to the structures). <b>struct
+ml_frame_description</b> contains the following
+fields:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fn</b>: The function which is called generate the frame
+(or nested frameset). This function must call either
+<b>new_ml_window</b> or <b>new_ml_frameset</b>. The function
+is prototyped as <b>void fn (ml_session session, void
+*data);</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>data</b>: Data pointer passed to this
+function.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_set_description</b> allows the frameset
+description to be updated.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_(set|get)_title</b> updates the window
+title.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_window_set_stylesheet.3.html b/doc/ml_window_set_stylesheet.3.html
new file mode 100644 (file)
index 0000000..c212129
--- /dev/null
@@ -0,0 +1,205 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_window</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_window</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:09 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_window, ml_window_pack, ml_window_set_title, ml_window_get_title, ml_window_set_stylesheet, ml_window_get_stylesheet, ml_window_set_charset, ml_window_get_charset, new_ml_frameset, ml_frameset_set_description, ml_frameset_set_title, ml_frameset_get_title - monolith window and frameset</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_window.h&gt;
+
+ml_window new_ml_window (struct ml_session *, pool pool);
+void ml_window_pack (ml_window, ml_widget);
+void ml_window_set_title (ml_window, const char *title);
+const char *ml_window_get_title (ml_window);
+void ml_window_set_stylesheet (ml_window, const char *stylesheet);
+const char *ml_window_get_stylesheet (ml_window);
+void ml_window_set_charset (ml_window, const char *charset);
+const char *ml_window_get_charset (ml_window);
+ml_window new_ml_frameset (struct ml_session *, pool pool, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_description (ml_window, struct ml_session *, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_title (ml_window, const char *);
+const char *ml_frameset_get_title (ml_window);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;window&quot; is a top-level window. Every
+application has at least one window, created in the main
+function. A window is just a wrapper. To actually do
+anything, you must pack a single widget inside the window.
+Windows can only take a single widget. If you want more than
+one widget to appear inside a window, then you must pack
+them into some sort of layout widget first, and pack the
+layout widget into the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;frameset&quot; is used to create framesets (several
+ordinary windows packed into one top-level window, with
+independent scrolling capabilities). Windows and framesets
+are actually so similar, that I have included them in the
+same class (and also so that other code can deal with an
+opaque <b>ml_window</b> pointer and not have to worry about
+whether it is a window or a frameset). Framesets may contain
+windows or other framesets, or a mixture of
+both.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Monolith windows are not widgets (unlike many of the other
+classes in monolith).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_window</b> creates a new monolith
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_pack</b> packs a widget into the window. Since
+a window can only contain a single widget, subsequent calls
+to this function overwrite the packed widget. (Note: this
+call does not apply to framesets).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_title</b> changes the title of the
+window. The title of the window defaults to no title at all,
+so it is a good idea to set this.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_stylesheet</b> changes the stylesheet
+of the page. Monolith default stylesheets are installed in
+<b>/ml-styles/</b>, and the default stylesheet is
+<b>/ml-styles/default.css</b> (supplied in the monolith
+distribution). Stylesheets are used to 'theme' monolith
+applications.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_charset</b> changes the character
+encoding associated with the window. Because of limitations
+in HTML, only a single charset can be associated with all of
+the widgets in a window. The default charset is
+<b>iso-8859-1</b>. For multilingual support, it is
+recommended that users change the charset to either their
+native language encoding, or to <b>utf-8</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_frameset</b> creates a new frameset. <b>rows</b>
+and <b>cols</b> define the number of frames and their
+layout. You can use <b>rows</b> and <b>cols</b> to create
+frameset layouts including grids, as described in the HTML 4
+standard (<b>http://www.w3.org/TR/html401/</b>). To create
+nested framesets, use a frame which generates a frameset
+instead of a window. <b>frames</b> is a vector of <b>struct
+ml_frame_description</b> structures (note: the structures
+themselves, not pointers to the structures). <b>struct
+ml_frame_description</b> contains the following
+fields:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fn</b>: The function which is called generate the frame
+(or nested frameset). This function must call either
+<b>new_ml_window</b> or <b>new_ml_frameset</b>. The function
+is prototyped as <b>void fn (ml_session session, void
+*data);</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>data</b>: Data pointer passed to this
+function.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_set_description</b> allows the frameset
+description to be updated.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_(set|get)_title</b> updates the window
+title.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/ml_window_set_title.3.html b/doc/ml_window_set_title.3.html
new file mode 100644 (file)
index 0000000..c212129
--- /dev/null
@@ -0,0 +1,205 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_window</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_window</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:09 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_window, ml_window_pack, ml_window_set_title, ml_window_get_title, ml_window_set_stylesheet, ml_window_get_stylesheet, ml_window_set_charset, ml_window_get_charset, new_ml_frameset, ml_frameset_set_description, ml_frameset_set_title, ml_frameset_get_title - monolith window and frameset</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_window.h&gt;
+
+ml_window new_ml_window (struct ml_session *, pool pool);
+void ml_window_pack (ml_window, ml_widget);
+void ml_window_set_title (ml_window, const char *title);
+const char *ml_window_get_title (ml_window);
+void ml_window_set_stylesheet (ml_window, const char *stylesheet);
+const char *ml_window_get_stylesheet (ml_window);
+void ml_window_set_charset (ml_window, const char *charset);
+const char *ml_window_get_charset (ml_window);
+ml_window new_ml_frameset (struct ml_session *, pool pool, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_description (ml_window, struct ml_session *, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_title (ml_window, const char *);
+const char *ml_frameset_get_title (ml_window);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;window&quot; is a top-level window. Every
+application has at least one window, created in the main
+function. A window is just a wrapper. To actually do
+anything, you must pack a single widget inside the window.
+Windows can only take a single widget. If you want more than
+one widget to appear inside a window, then you must pack
+them into some sort of layout widget first, and pack the
+layout widget into the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;frameset&quot; is used to create framesets (several
+ordinary windows packed into one top-level window, with
+independent scrolling capabilities). Windows and framesets
+are actually so similar, that I have included them in the
+same class (and also so that other code can deal with an
+opaque <b>ml_window</b> pointer and not have to worry about
+whether it is a window or a frameset). Framesets may contain
+windows or other framesets, or a mixture of
+both.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Monolith windows are not widgets (unlike many of the other
+classes in monolith).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_window</b> creates a new monolith
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_pack</b> packs a widget into the window. Since
+a window can only contain a single widget, subsequent calls
+to this function overwrite the packed widget. (Note: this
+call does not apply to framesets).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_title</b> changes the title of the
+window. The title of the window defaults to no title at all,
+so it is a good idea to set this.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_stylesheet</b> changes the stylesheet
+of the page. Monolith default stylesheets are installed in
+<b>/ml-styles/</b>, and the default stylesheet is
+<b>/ml-styles/default.css</b> (supplied in the monolith
+distribution). Stylesheets are used to 'theme' monolith
+applications.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_charset</b> changes the character
+encoding associated with the window. Because of limitations
+in HTML, only a single charset can be associated with all of
+the widgets in a window. The default charset is
+<b>iso-8859-1</b>. For multilingual support, it is
+recommended that users change the charset to either their
+native language encoding, or to <b>utf-8</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_frameset</b> creates a new frameset. <b>rows</b>
+and <b>cols</b> define the number of frames and their
+layout. You can use <b>rows</b> and <b>cols</b> to create
+frameset layouts including grids, as described in the HTML 4
+standard (<b>http://www.w3.org/TR/html401/</b>). To create
+nested framesets, use a frame which generates a frameset
+instead of a window. <b>frames</b> is a vector of <b>struct
+ml_frame_description</b> structures (note: the structures
+themselves, not pointers to the structures). <b>struct
+ml_frame_description</b> contains the following
+fields:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fn</b>: The function which is called generate the frame
+(or nested frameset). This function must call either
+<b>new_ml_window</b> or <b>new_ml_frameset</b>. The function
+is prototyped as <b>void fn (ml_session session, void
+*data);</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>data</b>: Data pointer passed to this
+function.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_set_description</b> allows the frameset
+description to be updated.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_(set|get)_title</b> updates the window
+title.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_box.3.html b/doc/new_ml_box.3.html
new file mode 100644 (file)
index 0000000..3d8287f
--- /dev/null
@@ -0,0 +1,89 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_box</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_box</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:09 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_box, ml_box_pack - monolith box widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_box.h&gt;
+
+ml_box new_ml_box (pool pool);
+void ml_box_pack (ml_box, ml_widget);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The monolith box widget encloses another widget inside a
+rectangular outline.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_box</b> creates a new box widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_box_pack</b> packs another widget inside the box. A
+maximum of one widget can be packed inside a box, so if you
+call this function multiple times, then earlier widgets will
+be forgotten.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_button.3.html b/doc/new_ml_button.3.html
new file mode 100644 (file)
index 0000000..9c00968
--- /dev/null
@@ -0,0 +1,147 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_button</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_button</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:09 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_button, ml_button_set_text, ml_button_get_text, ml_button_set_callback, ml_button_set_key - monolith button widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_button.h&gt;
+
+ml_button new_ml_button (pool pool, const char *text);
+void ml_button_set_text (ml_button, const char *text);
+const char *ml_button_get_text (ml_button);
+void ml_button_set_callback (ml_button, void (*fn)(ml_session, void *), ml_session, void *);
+void ml_button_set_key (ml_button, int is_key);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A button widget is a simple button which calls a function
+when clicked.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_button</b> creates a new button
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_(set|get)_text</b> updates the HTML text
+printed on the button. Note that the text string must be
+either static, or already allocated in the button's pool, or
+allocated in a pool with a longer lifetime than the button.
+If the text is set to <b>NULL</b> then this has the same
+effect as setting the text to the empty string, which is not
+very useful because the button can never be
+pressed.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_callback</b> updates the callback function
+which is invoked when the button is pressed.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_key</b> sets the boolean &quot;key&quot;
+property of the button. This should be set on buttons where
+the text is a single letter, suitable for, say, calculator
+keys. All key buttons are rendered at a fixed width of
+&quot;1em&quot;, resulting in a more pleasing overall
+effect.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_popup</b> sets the popup property of the
+button. When the user clicks on a popup button, a new popup
+window opens on the screen. To make a button into a popup
+button, the popup property should be a non-<b>NULL</b>
+string which is the name of the new browser window (note
+that because of limitations in HTML, browser window names
+are global, so it is recommended that names be
+&quot;<b>appname</b>_<b>name</b>&quot;). To change a popup
+button back to an ordinary button, set the popup property to
+<b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The callback used by a popup button must create a top-level
+window, otherwise you will get an internal server
+error.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_button_set_popup_size</b> changes the size of the
+popup window. The default is width 0, height 0, which
+usually creates a popup window which is the same size and
+shape as the current browser window.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_dialog.3.html b/doc/new_ml_dialog.3.html
new file mode 100644 (file)
index 0000000..bf9f662
--- /dev/null
@@ -0,0 +1,161 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_dialog</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_dialog</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:10 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_dialog, ml_dialog_set_text, ml_dialog_get_text, ml_dialog_set_title, ml_dialog_get_title, ml_dialog_set_icon, ml_dialog_get_icon, ml_dialog_clear_buttons, ml_dialog_add_button - monolith dialog widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_dialog.h&gt;
+
+ml_dialog new_ml_dialog (pool pool);
+void ml_dialog_set_text (ml_dialog, const char *text);
+const char *ml_dialog_get_text (ml_dialog);
+void ml_dialog_set_title (ml_dialog, const char *title);
+const char *ml_dialog_get_title (ml_dialog);
+void ml_dialog_set_icon (ml_dialog, const char *icon);
+const char *ml_dialog_get_icon (ml_dialog);
+void ml_dialog_clear_buttons (ml_dialog);
+void ml_dialog_add_button (ml_dialog, const char *text, void (*callback_fn) (ml_session, void *), ml_session session, void *data);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A dialog is a widget for asking a user a question, and
+getting an answer. It can also be used for presenting the
+user with confirmation that some operation has been carried
+out, or for presenting the user with an error
+message.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Fundamentally, a dialog consists of some text, and a series
+of zero or more buttons along the bottom of the widget. A
+dialog can also have an optional title which appears along
+the top and an optional icon which appears on the left of
+the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Dialogs are almost always used packed directly into a
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Dialogs are actually &quot;super-widgets&quot;, built up
+from fundamental widgets like labels, buttons, and table
+layouts. However currently none of the fundamental widgets
+are actually exposed through the <b>ml_dialog_*</b>
+interface.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_dialog</b> creates a new dialog widget with no
+text, no title, no icon and no buttons (ie. not very useful!
+- after calling this you should immediately call
+<b>ml_dialog_set_text</b> and probably
+<b>ml_dialog_add_button</b> too).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_text</b> updates the text in a dialog
+widget. Although having text is not strictly mandatory, it
+is highly advisable.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_title</b> changes the title. To have
+no title, set the title to <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_(set|get)_icon</b> changes the icon. To have no
+icon, set the icon to <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_clear_buttons</b> removes all the buttons from
+the dialog.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_dialog_add_button</b> adds a single button to the
+dialog. If there are already buttons attached to the dialog,
+then this adds the new button on the right. The <b>text</b>
+which appears on the button must be specified. When the
+button is pressed, <b>callback_fn</b> will be
+called.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_flow_layout.3.html b/doc/new_ml_flow_layout.3.html
new file mode 100644 (file)
index 0000000..b96c648
--- /dev/null
@@ -0,0 +1,124 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_flow_layout</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_flow_layout</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:10 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_flow_layout, ml_flow_layout_push_back, ml_flow_layout_pop_back, ml_flow_layout_push_front, ml_flow_layout_pop_front, ml_flow_layout_get, ml_flow_layout_insert, ml_flow_layout_replace, ml_flow_layout_erase, ml_flow_layout_clear, ml_flow_layout_size, ml_flow_layout_pack - monolith flow_layout widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_flow_layout.h&gt;
+
+ml_flow_layout new_ml_flow_layout (pool pool);
+void ml_flow_layout_push_back (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_back (ml_flow_layout);
+void ml_flow_layout_push_front (ml_flow_layout, ml_widget);
+ml_widget ml_flow_layout_pop_front (ml_flow_layout);
+ml_widget ml_flow_layout_get (ml_flow_layout, int i);
+void ml_flow_layout_insert (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_replace (ml_flow_layout, int i, ml_widget);
+void ml_flow_layout_erase (ml_flow_layout, int i);
+void ml_flow_layout_clear (ml_flow_layout);
+int ml_flow_layout_size (ml_flow_layout);
+void ml_flow_layout_pack (ml_flow_layout, ml_widget);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A flow layout widget is the simplest type of layout widget.
+It contains an ordered list of widgets, and it simply
+arranges them one after another (in other words with no
+&quot;layout&quot; at all).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_flow_layout</b> creates a new flow layout
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Underlying the flow layout widget is a simple c2lib vector,
+and the other access functions use the same notation as the
+equivalent c2lib <b>vector_*</b> functions. Go to the SEE
+ALSO section below to see how to manipulate widgets within a
+flow layout.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_flow_layout_pack</b> is equivalent to
+<b>ml_flow_layout_push_back</b>: it appends the widget to
+the end of the current vector of widgets.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_form.3.html b/doc/new_ml_form.3.html
new file mode 100644 (file)
index 0000000..f281e2e
--- /dev/null
@@ -0,0 +1,153 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:10 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form - monolith form widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form.h&gt;
+
+ml_form new_ml_form (pool pool);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A monolith form widget is a low-level widget for handling
+input of text fields, check buttons, radio buttons and so
+on, collected together into a single form on a
+page.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A form should contain a mixture of input widgets and
+ordinary widgets. Since the form widget itself can only be
+packed with a single widget, normally you would pack a
+layout widget directly into the form, and then a mixture of
+labels and input widgets into the layout
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+One or more of the widgets packed into the form can be a
+submit button (see <b>new_ml_form_submit(3)</b>). Pressing
+on the submit button causes the form's callback function to
+be called, and in this function the values entered into the
+other input widgets can be read. It is recommended that all
+forms contain at least one submit button, because the effect
+of creating a form with no submit buttons is
+browser-dependent, and can mean that the form cannot be
+submitted.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Forms cannot be nested (a form widget cannot contain another
+form widget inside itself). This is a limitation of
+HTML.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Forms are low-level entities. To do seriously interesting
+things with forms such as self-validation, use one of the
+higher-level form-type abstractions that monolith provides
+(XXX will provide, not now - RWMJ).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form</b> creates a new form widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_set_callback</b> sets the callback function which
+is called when the form is submitted. The callback function
+is invoked as <b>void callback_fn (ml_session session, void
+*data)</b>. The values that the user entered in the input
+fields are available by calling (for example)
+<b>ml_form_input_get_value(3)</b> on each input widget
+within the form.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_pack</b> packs a widget into the form. A form can
+only store a single widget, so if you call pack subsequent
+times, the earlier widgets are forgotten.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_input(3)</b>, <b>new_ml_form_textarea(3)</b>,
+<b>new_ml_form_submit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_form_checkbox.3.html b/doc/new_ml_form_checkbox.3.html
new file mode 100644 (file)
index 0000000..90d0929
--- /dev/null
@@ -0,0 +1,91 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_checkbox</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_checkbox</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:10 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_checkbox - monolith form checkbox input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_checkbox.h&gt;
+
+ml_form_checkbox new_ml_form_checkbox (pool pool, ml_form form);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a checkbox (tickbox) for use in forms.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_checkbox</b> creates a new form checkbox
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b>
+parameter.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>,
+<b>ml_form_input_get_value(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_form_password.3.html b/doc/new_ml_form_password.3.html
new file mode 100644 (file)
index 0000000..1124034
--- /dev/null
@@ -0,0 +1,94 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_password</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_password</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:11 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_password - monolith form password input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_password.h&gt;
+
+ml_form_password new_ml_form_password (pool pool, ml_form form);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a single line text input field, for use in forms.
+The field is rendered by the browser obscured (often with
+&quot;*&quot; for characters). However it is passed over the
+network in plain text.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_password</b> creates a new form password
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b>
+parameter.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>,
+<b>ml_form_input_get_value(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_form_radio.3.html b/doc/new_ml_form_radio.3.html
new file mode 100644 (file)
index 0000000..df8798c
--- /dev/null
@@ -0,0 +1,107 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_radio</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_radio</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:11 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_radio, ml_form_radio_set_checked, ml_form_radio_get_checked - monolith radio button widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_radio.h&gt;
+
+ml_form_radio new_ml_form_radio (pool pool, ml_form_radio_group group, const char *value);
+void ml_form_radio_set_checked (ml_form_radio w, int checked);
+int ml_form_radio_get_checked (ml_form_radio w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a radio button input, for use in forms. Radio
+buttons represent choices, and thus are grouped together.
+For this reason, you must place all related radio buttons
+inside a <b>ml_form_radio_group</b> widget, which itself
+goes inside the form.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_radio</b> creates a new radio button widget.
+The radio button group into which this widget is being
+embedded is passed as the <b>group</b>
+parameter.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+After the form has been submitted, you can see which button
+was pressed by checking the value of the
+<b>ml_form_radio_group</b> widget, or by looking at the
+checked status of each individual radio button using
+<b>ml_form_radio_(set|get)_checked</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>,
+<b>new_ml_form_radio_group(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_form_radio_group.3.html b/doc/new_ml_form_radio_group.3.html
new file mode 100644 (file)
index 0000000..b0efeec
--- /dev/null
@@ -0,0 +1,105 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_radio_group</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_radio_group</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:11 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_radio_group, ml_form_radio_group_pack - monolith group of radio buttons widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_radio_group.h&gt;
+
+ml_form_radio_group new_ml_form_radio_group (pool pool, ml_form form);
+void ml_form_radio_group_pack (ml_form_radio_group w, ml_widget);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A radio group is a widget which contains a group of related
+radio buttons. You cannot use radio buttons
+&quot;naked&quot; in a form, but instead must embed related
+buttons inside one of these widgets.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_radio_group</b> creates a new group
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_radio_group_pack</b> packs a single widget inside
+the radio group widget. A radio group can only contain a
+single widget, so if you call pack again, it will forget the
+previous widget. It is recommended that you pack either a
+flow layout or a table layout directly inside the radio
+group, and then pack the actual radio buttons inside
+that.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>new_ml_form_radio(3)</b>,
+<b>ml_form_input_get_value(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_form_select.3.html b/doc/new_ml_form_select.3.html
new file mode 100644 (file)
index 0000000..e9c381a
--- /dev/null
@@ -0,0 +1,183 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_select</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_select</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:11 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_select, ml_form_select_set_size, ml_form_select_get_size, ml_form_select_set_multiple, ml_form_select_get_multiple, ml_form_select_push_back, ml_form_select_pop_back, ml_form_select_push_front, ml_form_select_pop_front, ml_form_select_get, ml_form_select_insert, ml_form_select_replace, ml_form_select_erase, ml_form_select_clear, ml_form_select_size, ml_form_select_set_selection, ml_form_select_set_selections, ml_form_select_get_selection, ml_form_select_get_selections - monolith form select box input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_select.h&gt;
+
+ml_form_select new_ml_form_select (pool pool, ml_form form);
+void ml_form_select_set_size (ml_form_select w, int size);
+int ml_form_select_get_size (ml_form_select w);
+void ml_form_select_set_multiple (ml_form_select w, int multiple);
+int ml_form_select_get_multiple (ml_form_select w);
+void ml_form_select_push_back (ml_form_select w, const char *option);
+const char *ml_form_select_pop_back (ml_form_select w);
+void ml_form_select_push_front (ml_form_select w, const char *option);
+const char *ml_form_select_pop_front (ml_form_select w);
+const char *ml_form_select_get (ml_form_select w, int option_index);
+void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+void ml_form_select_erase (ml_form_select w, int option_index);
+void ml_form_select_clear (ml_form_select w);
+int ml_form_select_size (ml_form_select w);
+void ml_form_select_set_selection (ml_form_select w, int option_index);
+void ml_form_select_set_selections (ml_form_select w, vector selected);
+int ml_form_select_get_selection (ml_form_select w);
+const vector ml_form_select_get_selections (ml_form_select w);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a select box for use in forms. It can appear in
+several ways: either as a drop-down menu, or as a selection
+box allowing single or multiple options to be
+selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_select</b> creates a new form select box
+input widget. The form into which this widget is being
+embedded is passed as the <b>form</b> parameter. The select
+box is created with no options, in drop-down mode (size 0),
+single choice (multiple 0).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_size</b> sets the size (number
+of rows) in the select box. If the size is 0, then the
+select box will be rendered as a drop-down menu. If the size
+is &gt; 0, then the select box will be rendered as a
+scrolling list of choices.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_form_select_(set|get)_multiple</b> sets the multiple
+boolean property of the select box. If this is false (the
+default), then only a single option can be selected. If this
+is true, then multiple options can be selected by the
+user.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To add options to the select box, use the
+<b>ml_form_select_push_back</b> and other access functions.
+These are modelled on the c2lib <b>vector_*</b>
+functions.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To choose which option is selected first in a single
+selection select box, call
+<b>ml_form_select_set_selection</b>. For select boxes which
+allow multiple selections, prepare a <b>vector</b> of
+<b>int</b> which is at least as long as the number of
+options. Each element of the vector should be a boolean
+value saying whether the corresponding option is selected or
+not. Pass this to
+<b>ml_form_select_set_selections</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows only single selections, then after
+the form has been submitted by the user, you can read back
+the index of the option which was selected using
+<b>ml_form_select_get_selection</b>. This returns -1 if
+nothing was selected.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If the select box allows multiple selections, then call
+<b>ml_form_select_get_selections</b> which returns a vector
+of boolean values (<b>vector</b> of <b>int</b>) of exactly
+the same length as the number of options. This vector will
+contain true corresponding to every selected option. This
+function may also return <b>NULL</b>, indicating that
+nothing was selected (or the form wasn't
+submitted).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>, <b>ml_form_input_get_value(3)</b>,
+<b>vector_push_back(3)</b>, <b>vector_pop_back(3)</b>,
+<b>vector_push_front(3)</b>, <b>vector_pop_front(3)</b>,
+<b>vector_get(3)</b>, <b>vector_insert(3)</b>,
+<b>vector_replace(3)</b>, <b>vector_erase(3)</b>,
+<b>vector_clear(3)</b>, <b>vector_size(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_form_submit.3.html b/doc/new_ml_form_submit.3.html
new file mode 100644 (file)
index 0000000..1dd0859
--- /dev/null
@@ -0,0 +1,92 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_submit</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_submit</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:11 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_submit - monolith form submit button widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_submit.h&gt;
+
+ml_form_submit new_ml_form_submit (pool pool, ml_form form, const char *value);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a submit button, which can only be used within forms
+(see <b>new_ml_form(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_submit</b> creates a new form submit button.
+The form into which this widget is being embedded is passed
+as the <b>form</b> parameter. The <b>value</b> parameter is
+what is displayed on the button.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>,
+<b>ml_form_input_get_value(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_form_text.3.html b/doc/new_ml_form_text.3.html
new file mode 100644 (file)
index 0000000..b9990ad
--- /dev/null
@@ -0,0 +1,91 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_text</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_text</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:12 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_text - monolith form text input widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_text.h&gt;
+
+ml_form_text new_ml_form_text (pool pool, ml_form form);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a single line text input field, which can only be
+used within forms (see <b>new_ml_form(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_text</b> creates a new form text input
+widget. The form into which this widget is being embedded is
+passed as the <b>form</b> parameter.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>,
+<b>ml_form_input_get_value(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_form_textarea.3.html b/doc/new_ml_form_textarea.3.html
new file mode 100644 (file)
index 0000000..0fd3f53
--- /dev/null
@@ -0,0 +1,93 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_form_textarea</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_form_textarea</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:12 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_form_textarea - monolith form textarea widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_form_textarea.h&gt;
+
+ml_form_textarea new_ml_form_textarea (pool pool, ml_form form, int rows, int cols);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+This is a multiple line text input field, which can only be
+used within forms (see <b>new_ml_form(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form_textarea</b> creates a new form textarea
+widget. The form into which this widget is being embedded is
+passed as the <b>form</b> parameter. The size of this input
+field is given by the <b>rows</b> and <b>cols</b> parameters
+(both measured in number of characters).</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_form(3)</b>,
+<b>ml_form_input_get_value(3)</b></td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_frameset.3.html b/doc/new_ml_frameset.3.html
new file mode 100644 (file)
index 0000000..1ebbf1d
--- /dev/null
@@ -0,0 +1,205 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_window</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_window</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:12 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_window, ml_window_pack, ml_window_set_title, ml_window_get_title, ml_window_set_stylesheet, ml_window_get_stylesheet, ml_window_set_charset, ml_window_get_charset, new_ml_frameset, ml_frameset_set_description, ml_frameset_set_title, ml_frameset_get_title - monolith window and frameset</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_window.h&gt;
+
+ml_window new_ml_window (struct ml_session *, pool pool);
+void ml_window_pack (ml_window, ml_widget);
+void ml_window_set_title (ml_window, const char *title);
+const char *ml_window_get_title (ml_window);
+void ml_window_set_stylesheet (ml_window, const char *stylesheet);
+const char *ml_window_get_stylesheet (ml_window);
+void ml_window_set_charset (ml_window, const char *charset);
+const char *ml_window_get_charset (ml_window);
+ml_window new_ml_frameset (struct ml_session *, pool pool, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_description (ml_window, struct ml_session *, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_title (ml_window, const char *);
+const char *ml_frameset_get_title (ml_window);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;window&quot; is a top-level window. Every
+application has at least one window, created in the main
+function. A window is just a wrapper. To actually do
+anything, you must pack a single widget inside the window.
+Windows can only take a single widget. If you want more than
+one widget to appear inside a window, then you must pack
+them into some sort of layout widget first, and pack the
+layout widget into the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;frameset&quot; is used to create framesets (several
+ordinary windows packed into one top-level window, with
+independent scrolling capabilities). Windows and framesets
+are actually so similar, that I have included them in the
+same class (and also so that other code can deal with an
+opaque <b>ml_window</b> pointer and not have to worry about
+whether it is a window or a frameset). Framesets may contain
+windows or other framesets, or a mixture of
+both.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Monolith windows are not widgets (unlike many of the other
+classes in monolith).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_window</b> creates a new monolith
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_pack</b> packs a widget into the window. Since
+a window can only contain a single widget, subsequent calls
+to this function overwrite the packed widget. (Note: this
+call does not apply to framesets).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_title</b> changes the title of the
+window. The title of the window defaults to no title at all,
+so it is a good idea to set this.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_stylesheet</b> changes the stylesheet
+of the page. Monolith default stylesheets are installed in
+<b>/ml-styles/</b>, and the default stylesheet is
+<b>/ml-styles/default.css</b> (supplied in the monolith
+distribution). Stylesheets are used to 'theme' monolith
+applications.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_charset</b> changes the character
+encoding associated with the window. Because of limitations
+in HTML, only a single charset can be associated with all of
+the widgets in a window. The default charset is
+<b>iso-8859-1</b>. For multilingual support, it is
+recommended that users change the charset to either their
+native language encoding, or to <b>utf-8</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_frameset</b> creates a new frameset. <b>rows</b>
+and <b>cols</b> define the number of frames and their
+layout. You can use <b>rows</b> and <b>cols</b> to create
+frameset layouts including grids, as described in the HTML 4
+standard (<b>http://www.w3.org/TR/html401/</b>). To create
+nested framesets, use a frame which generates a frameset
+instead of a window. <b>frames</b> is a vector of <b>struct
+ml_frame_description</b> structures (note: the structures
+themselves, not pointers to the structures). <b>struct
+ml_frame_description</b> contains the following
+fields:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fn</b>: The function which is called generate the frame
+(or nested frameset). This function must call either
+<b>new_ml_window</b> or <b>new_ml_frameset</b>. The function
+is prototyped as <b>void fn (ml_session session, void
+*data);</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>data</b>: Data pointer passed to this
+function.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_set_description</b> allows the frameset
+description to be updated.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_(set|get)_title</b> updates the window
+title.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_image.3.html b/doc/new_ml_image.3.html
new file mode 100644 (file)
index 0000000..f8e1e86
--- /dev/null
@@ -0,0 +1,101 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_image</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_image</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:12 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_image, ml_image_set_src, ml_image_get_src - monolith image widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_image.h&gt;
+
+ml_image new_ml_image (pool pool, const char *src);
+void ml_image_set_src (ml_image, const char *src);
+const char *ml_image_get_src (ml_image);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+An image widget displays a graphical image, taken from a
+particular source (in fact, it corresponds almost exactly to
+the HTML <b>&lt;img/&gt;</b> element).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The current implementation of <b>ml_image</b> is rather
+immature. In future we will support image sizes, alt, title,
+longdesc, and so on.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_image</b> creates a new image. You should supply
+the <b>src</b> (source) for the image, which is an absolute
+or relative link. If <b>src</b> is set to <b>NULL</b> then
+no image is displayed.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_image_(set|get)_src</b> updates the source of the
+image. You may set the source to <b>NULL</b> to display no
+image at all.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_label.3.html b/doc/new_ml_label.3.html
new file mode 100644 (file)
index 0000000..02ca7a2
--- /dev/null
@@ -0,0 +1,91 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_label</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_label</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:13 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_label, ml_label_set_text, ml_label_get_text - monolith label widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_label.h&gt;
+
+ml_label new_ml_label (pool pool, const char *text);
+void ml_label_set_text (ml_label, const char *text);
+const char *ml_label_get_text (ml_label);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A label widget is a generic piece of HTML.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_label</b> creates a new label widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_label_(set|get)_text</b> updates the HTML associated
+with the label. Note that the text string must be either
+static, or already allocated in the label's pool, or
+allocated in a pool with a longer lifetime than the label.
+If the text is set to <b>NULL</b> then this has the same
+effect as setting the text to the empty string.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_table_layout.3.html b/doc/new_ml_table_layout.3.html
new file mode 100644 (file)
index 0000000..e15203c
--- /dev/null
@@ -0,0 +1,142 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_table_layout</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_table_layout</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:13 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_table_layout, ml_table_layout_pack, ml_table_layout_set_colspan, ml_table_layout_set_rowspan, ml_table_layout_set_align, ml_table_layout_set_valign - monolith table layout widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_table_layout.h&gt;
+
+ml_table_layout new_ml_table_layout (pool, int rows, int cols);
+void ml_table_layout_pack (ml_table_layout, ml_widget, int row, int col);
+void ml_table_layout_set_colspan (ml_table_layout, int row, int col, int colspan);
+void ml_table_layout_set_rowspan (ml_table_layout, int row, int col, int rowspan);
+void ml_table_layout_set_align (ml_table_layout, int row, int col, const char *align);
+void ml_table_layout_set_valign (ml_table_layout, int row, int col, const char *valign);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The monolith table layout widget is a very powerful layout
+tool. It is modelled on, and indeed implemented using, HTML
+&lt;table&gt;s. Table layouts are grids of widgets with a
+fixed number of rows and columns. Each widget normally
+occupies a single cell of the table, but widgets may occupy
+a rectangle of several adjacent cells. All cells in the
+table are referenced using the row and column number, with
+row and column numbers starting from zero.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_table_layout</b> creates a new table layout widget
+with <b>rows</b> cells across and <b>cols</b> cells down.
+The cells are numbered <b>0 .. rows-1</b> across and code{0
+.. cols-1} down.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_pack</b> packs a widget at position
+<b>(row,col)</b> within the table. To remove a widget and
+leave a cell empty, pack a <b>NULL</b> widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_set_colspan</b> and
+<b>ml_table_layout_set_rowspan</b> set the column span and
+row span for a particular table cell respectively. The
+col/row-span allow a cell to occupy one or more adjacent
+cells in the table (any widgets packed in those adjacent
+cells are silently ignored). The default column and row span
+for every cell is 1.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_set_align</b> sets the horizontal
+alignment for the content of a cell. The possibilities are
+<b>&quot;left&quot;</b>, <b>&quot;center&quot;</b> or
+<b>&quot;right&quot;</b>, with the default being
+left-aligned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_table_layout_set_valign</b> sets the vertical
+alignment for the content of a cell. The possibilities are
+<b>&quot;top&quot;</b>, <b>&quot;middle&quot;</b> or
+<b>&quot;bottom&quot;</b>, with the default being
+middle.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_widget(3)</b>.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_text_label.3.html b/doc/new_ml_text_label.3.html
new file mode 100644 (file)
index 0000000..a6ba202
--- /dev/null
@@ -0,0 +1,138 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_text_label</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_text_label</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:13 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_text_label, ml_text_label_set_text_align, ml_text_label_set_font_weight, ml_text_label_set_font_size, ml_text_label_set_text - monolith text label widget</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_text_label.h&gt;
+
+ml_text_label new_ml_text_label (pool, const char *text);
+void ml_text_label_set_text_align (ml_text_label, const char *);
+void ml_text_label_set_font_weight (ml_text_label, const char *);
+void ml_text_label_set_font_size (ml_text_label, const char *);
+void ml_text_label_set_text (ml_text_label, const char *);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Text labels are simple strings or paragraphs of text. Unlike
+the <b>ml_label</b> widget, HTML sequences are escaped
+before being sent to the browser, so <b>ml_text_label</b>s
+can contain <b>&lt;</b>, <b>&gt;</b>, <b>&amp;</b> and so on
+and these will be displayed correctly.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Text labels allow control over the style, size and other
+aspects of how the text appears.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_text_label</b> creates a new text label
+widget.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_set_text_align</b> sets the text-align
+style of the text label. Common alignments are
+<b>&quot;left&quot;</b>, <b>&quot;right&quot;</b> and
+<b>&quot;justify&quot;</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_set_font_weight</b> sets the font-weight
+style of the text label. Common weights are
+<b>&quot;normal&quot;</b> and
+<b>&quot;bold&quot;</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_set_font_size</b> sets the font-size style
+of the text label. Common sizes are
+<b>&quot;small&quot;</b>, <b>&quot;medium&quot;</b> and
+<b>&quot;large&quot;</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_text_label_(set|get)_text</b> updates the text
+displayed on the label.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_widget(3)</b>, W3C CSS2
+recommendation.</td></table>
+<hr>
+</body>
+</html>
diff --git a/doc/new_ml_window.3.html b/doc/new_ml_window.3.html
new file mode 100644 (file)
index 0000000..13c49bb
--- /dev/null
@@ -0,0 +1,205 @@
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ml_window</title>
+</head>
+<body>
+
+<h1 align=center>new_ml_window</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator     : groff version 1.17.2 -->
+<!-- CreationDate: Sat Aug 31 18:06:13 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ml_window, ml_window_pack, ml_window_set_title, ml_window_get_title, ml_window_set_stylesheet, ml_window_get_stylesheet, ml_window_set_charset, ml_window_get_charset, new_ml_frameset, ml_frameset_set_description, ml_frameset_set_title, ml_frameset_get_title - monolith window and frameset</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include &lt;ml_window.h&gt;
+
+ml_window new_ml_window (struct ml_session *, pool pool);
+void ml_window_pack (ml_window, ml_widget);
+void ml_window_set_title (ml_window, const char *title);
+const char *ml_window_get_title (ml_window);
+void ml_window_set_stylesheet (ml_window, const char *stylesheet);
+const char *ml_window_get_stylesheet (ml_window);
+void ml_window_set_charset (ml_window, const char *charset);
+const char *ml_window_get_charset (ml_window);
+ml_window new_ml_frameset (struct ml_session *, pool pool, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_description (ml_window, struct ml_session *, const char *rows, const char *cols, vector frames);
+void ml_frameset_set_title (ml_window, const char *);
+const char *ml_frameset_get_title (ml_window);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;window&quot; is a top-level window. Every
+application has at least one window, created in the main
+function. A window is just a wrapper. To actually do
+anything, you must pack a single widget inside the window.
+Windows can only take a single widget. If you want more than
+one widget to appear inside a window, then you must pack
+them into some sort of layout widget first, and pack the
+layout widget into the window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A &quot;frameset&quot; is used to create framesets (several
+ordinary windows packed into one top-level window, with
+independent scrolling capabilities). Windows and framesets
+are actually so similar, that I have included them in the
+same class (and also so that other code can deal with an
+opaque <b>ml_window</b> pointer and not have to worry about
+whether it is a window or a frameset). Framesets may contain
+windows or other framesets, or a mixture of
+both.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Monolith windows are not widgets (unlike many of the other
+classes in monolith).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_window</b> creates a new monolith
+window.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_pack</b> packs a widget into the window. Since
+a window can only contain a single widget, subsequent calls
+to this function overwrite the packed widget. (Note: this
+call does not apply to framesets).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_title</b> changes the title of the
+window. The title of the window defaults to no title at all,
+so it is a good idea to set this.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_stylesheet</b> changes the stylesheet
+of the page. Monolith default stylesheets are installed in
+<b>/ml-styles/</b>, and the default stylesheet is
+<b>/ml-styles/default.css</b> (supplied in the monolith
+distribution). Stylesheets are used to 'theme' monolith
+applications.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_window_(set|get)_charset</b> changes the character
+encoding associated with the window. Because of limitations
+in HTML, only a single charset can be associated with all of
+the widgets in a window. The default charset is
+<b>iso-8859-1</b>. For multilingual support, it is
+recommended that users change the charset to either their
+native language encoding, or to <b>utf-8</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_ml_frameset</b> creates a new frameset. <b>rows</b>
+and <b>cols</b> define the number of frames and their
+layout. You can use <b>rows</b> and <b>cols</b> to create
+frameset layouts including grids, as described in the HTML 4
+standard (<b>http://www.w3.org/TR/html401/</b>). To create
+nested framesets, use a frame which generates a frameset
+instead of a window. <b>frames</b> is a vector of <b>struct
+ml_frame_description</b> structures (note: the structures
+themselves, not pointers to the structures). <b>struct
+ml_frame_description</b> contains the following
+fields:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fn</b>: The function which is called generate the frame
+(or nested frameset). This function must call either
+<b>new_ml_window</b> or <b>new_ml_frameset</b>. The function
+is prototyped as <b>void fn (ml_session session, void
+*data);</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>data</b>: Data pointer passed to this
+function.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_set_description</b> allows the frameset
+description to be updated.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ml_frameset_(set|get)_title</b> updates the window
+title.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones &lt;rich@annexia.org&gt;</td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+       cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+monolith-1.0.0</td></table>
+<hr>
+</body>
+</html>
diff --git a/examples/01_label_and_button.c b/examples/01_label_and_button.c
new file mode 100644 (file)
index 0000000..f7ab2c0
--- /dev/null
@@ -0,0 +1,110 @@
+/* A very simple monolith program.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: 01_label_and_button.c,v 1.8 2002/11/02 18:53:46 rich Exp $
+ */
+
+#include <string.h>
+
+#include <pool.h>
+#include <pstring.h>
+#include <pthr_cgi.h>
+
+#include "monolith.h"
+#include "ml_window.h"
+#include "ml_flow_layout.h"
+#include "ml_label.h"
+#include "ml_button.h"
+
+/*----- The following standard boilerplate code must appear -----*/
+
+/* Main entry point to the app. */
+static void app_main (ml_session);
+
+int
+handle_request (rws_request rq)
+{
+  return ml_entry_point (rq, app_main);
+}
+
+/*----- End of standard boilerplate code -----*/
+
+/* Private per-session data. */
+struct data
+{
+  ml_label lb;                 /* Label. */
+  int count;                   /* Count of number of button presses. */
+};
+
+static void increment (ml_session, void *);
+static void update_label (pool pool, ml_label lb, int count);
+
+static void
+app_main (ml_session session)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data;
+  ml_window w;
+  ml_flow_layout lay;
+  ml_label lb;
+  ml_button b;
+
+  /* Create the private, per-session data area. */
+  data = pmalloc (pool, sizeof *data);
+  data->count = 0;
+
+  /* Create the top-level window. */
+  w = new_ml_window (session, pool);
+
+  /* Create the flow layout widget which will be packed into the window. */
+  lay = new_ml_flow_layout (pool);
+
+  /* Create the label and button. */
+  data->lb = lb = new_ml_label (pool, 0);
+  update_label (pool, data->lb, 0);
+
+  b = new_ml_button (pool, "Push me!");
+  ml_button_set_callback (b, increment, session, data);
+
+  /* Pack the label and button into the flow layout widget. */
+  ml_flow_layout_pack (lay, lb);
+  ml_flow_layout_pack (lay, b);
+
+  /* Pack the flow layout widget into the window. */
+  ml_window_pack (w, lay);
+}
+
+/* This callback is called whenever the button is pressed. */
+static void
+increment (ml_session session, void *vp)
+{
+  struct data *data = (struct data *) vp;
+
+  update_label (ml_session_pool (session), data->lb, ++data->count);
+}
+
+/* This function just updates the label, once from the main function, and
+ * again each time we press the button.
+ */
+static void
+update_label (pool pool, ml_label lb, int count)
+{
+  ml_widget_set_property (lb, "text",
+                         psprintf (pool,
+                                   "Button pressed: <b>%d</b><br>",
+                                   count));
+}
diff --git a/examples/02_toy_calculator.c b/examples/02_toy_calculator.c
new file mode 100644 (file)
index 0000000..a1ba768
--- /dev/null
@@ -0,0 +1,403 @@
+/* A toy calculator
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: 02_toy_calculator.c,v 1.9 2002/11/07 10:49:00 rich Exp $
+ */
+
+#include <string.h>
+
+#include <pool.h>
+#include <pstring.h>
+#include <pthr_cgi.h>
+
+#include "monolith.h"
+#include "ml_window.h"
+#include "ml_table_layout.h"
+#include "ml_text_label.h"
+#include "ml_box.h"
+#include "ml_button.h"
+
+/* Main entry point to the app. */
+static void app_main (ml_session);
+
+int
+handle_request (rws_request rq)
+{
+  return ml_entry_point (rq, app_main);
+}
+
+/* Private per-session data. */
+struct data
+{
+  ml_text_label disp;          /* The display. */
+  char digits[16];             /* Display digits (only 10+1 used). */
+  double reg;                  /* Hidden register. */
+  int op;                      /* Operation: PLUS, MINUS, TIMES, DIVIDE. */
+};
+
+/* Lots of callback functions for each button. */
+static void press_button_0 (ml_session, void *);
+static void press_button_1 (ml_session, void *);
+static void press_button_2 (ml_session, void *);
+static void press_button_3 (ml_session, void *);
+static void press_button_4 (ml_session, void *);
+
+static void press_button_5 (ml_session, void *);
+static void press_button_6 (ml_session, void *);
+static void press_button_7 (ml_session, void *);
+static void press_button_8 (ml_session, void *);
+static void press_button_9 (ml_session, void *);
+
+static void press_button_DOT (ml_session, void *);
+static void press_button_EQUALS (ml_session, void *);
+static void press_button_PLUS (ml_session, void *);
+static void press_button_MINUS (ml_session, void *);
+static void press_button_TIMES (ml_session, void *);
+static void press_button_DIVIDE (ml_session, void *);
+static void press_button_CLEAR (ml_session, void *);
+static void press_button_AC (ml_session, void *);
+
+/* These are used as indexes in the b[] array. */
+#define DOT 10
+#define EQUALS 11
+#define PLUS 12
+#define MINUS 13
+#define TIMES 14
+#define DIVIDE 15
+#define CLEAR 16
+#define AC 17
+
+static void
+app_main (ml_session session)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data;
+  ml_window w;
+  ml_box box;
+  ml_table_layout tbl;
+  ml_button b[18];
+  ml_text_label disp;
+
+  /* Create the private, per-session data area and save it in the
+   * session object.
+   */
+  data = pmalloc (pool, sizeof *data);
+
+  /* Create the top-level window. */
+  w = new_ml_window (session, pool);
+
+  /* Create the box surrounding the calculator. */
+  box = new_ml_box (pool);
+
+  /* A table layout widget is used to arrange the buttons and the screen.
+   * There are 6 rows, each with 4 columns.
+   */
+  tbl = new_ml_table_layout (pool, 6, 4);
+
+  /* Create the numeric buttons. */
+  b[0] = new_ml_button (pool, "0");
+  ml_button_set_callback (b[0], press_button_0, session, data);
+  ml_widget_set_property (b[0], "button.style", "key");
+  b[1] = new_ml_button (pool, "1");
+  ml_button_set_callback (b[1], press_button_1, session, data);
+  ml_widget_set_property (b[1], "button.style", "key");
+  b[2] = new_ml_button (pool, "2");
+  ml_button_set_callback (b[2], press_button_2, session, data);
+  ml_widget_set_property (b[2], "button.style", "key");
+  b[3] = new_ml_button (pool, "3");
+  ml_button_set_callback (b[3], press_button_3, session, data);
+  ml_widget_set_property (b[3], "button.style", "key");
+  b[4] = new_ml_button (pool, "4");
+  ml_button_set_callback (b[4], press_button_4, session, data);
+  ml_widget_set_property (b[4], "button.style", "key");
+
+  b[5] = new_ml_button (pool, "5");
+  ml_button_set_callback (b[5], press_button_5, session, data);
+  ml_widget_set_property (b[5], "button.style", "key");
+  b[6] = new_ml_button (pool, "6");
+  ml_button_set_callback (b[6], press_button_6, session, data);
+  ml_widget_set_property (b[6], "button.style", "key");
+  b[7] = new_ml_button (pool, "7");
+  ml_button_set_callback (b[7], press_button_7, session, data);
+  ml_widget_set_property (b[7], "button.style", "key");
+  b[8] = new_ml_button (pool, "8");
+  ml_button_set_callback (b[8], press_button_8, session, data);
+  ml_widget_set_property (b[8], "button.style", "key");
+  b[9] = new_ml_button (pool, "9");
+  ml_button_set_callback (b[9], press_button_9, session, data);
+  ml_widget_set_property (b[9], "button.style", "key");
+
+  /* Create the other buttons. */
+  b[DOT] = new_ml_button (pool, ".");
+  ml_button_set_callback (b[DOT], press_button_DOT, session, data);
+  ml_widget_set_property (b[DOT], "button.style", "key");
+  b[EQUALS] = new_ml_button (pool, "=");
+  ml_button_set_callback (b[EQUALS], press_button_EQUALS, session, data);
+  ml_widget_set_property (b[EQUALS], "button.style", "key");
+  b[PLUS] = new_ml_button (pool, "+");
+  ml_button_set_callback (b[PLUS], press_button_PLUS, session, data);
+  ml_widget_set_property (b[PLUS], "button.style", "key");
+  b[MINUS] = new_ml_button (pool, "-");
+  ml_button_set_callback (b[MINUS], press_button_MINUS, session, data);
+  ml_widget_set_property (b[MINUS], "button.style", "key");
+  b[TIMES] = new_ml_button (pool, "x");
+  ml_button_set_callback (b[TIMES], press_button_TIMES, session, data);
+  ml_widget_set_property (b[TIMES], "button.style", "key");
+  b[DIVIDE] = new_ml_button (pool, "/");
+  ml_button_set_callback (b[DIVIDE], press_button_DIVIDE, session, data);
+  ml_widget_set_property (b[DIVIDE], "button.style", "key");
+  b[CLEAR] = new_ml_button (pool, "C");
+  ml_button_set_callback (b[CLEAR], press_button_CLEAR, session, data);
+  ml_widget_set_property (b[CLEAR], "button.style", "key");
+  b[AC] = new_ml_button (pool, "AC");
+  ml_button_set_callback (b[AC], press_button_AC, session, data);
+
+  /* Create the display. */
+  disp = new_ml_text_label (pool, "0");
+  ml_widget_set_property (disp, "font.weight", "bold");
+  ml_widget_set_property (disp, "font.size", "large");
+
+  /* Pack the buttons and display into the table layout widget. */
+  ml_table_layout_pack (tbl, disp, 0, 0);
+  ml_table_layout_set_colspan (tbl, 0, 0, 4);
+  ml_table_layout_set_align (tbl, 0, 0, "right");
+
+  ml_table_layout_pack (tbl, b[CLEAR], 1, 2);
+  ml_table_layout_pack (tbl, b[AC], 1, 3);
+
+  ml_table_layout_pack (tbl, b[7], 2, 0);
+  ml_table_layout_pack (tbl, b[8], 2, 1);
+  ml_table_layout_pack (tbl, b[9], 2, 2);
+  ml_table_layout_pack (tbl, b[DIVIDE], 2, 3);
+
+  ml_table_layout_pack (tbl, b[4], 3, 0);
+  ml_table_layout_pack (tbl, b[5], 3, 1);
+  ml_table_layout_pack (tbl, b[6], 3, 2);
+  ml_table_layout_pack (tbl, b[TIMES], 3, 3);
+
+  ml_table_layout_pack (tbl, b[1], 4, 0);
+  ml_table_layout_pack (tbl, b[2], 4, 1);
+  ml_table_layout_pack (tbl, b[3], 4, 2);
+  ml_table_layout_pack (tbl, b[MINUS], 4, 3);
+
+  ml_table_layout_pack (tbl, b[DOT], 5, 0);
+  ml_table_layout_pack (tbl, b[0], 5, 1);
+  ml_table_layout_pack (tbl, b[EQUALS], 5, 2);
+  ml_table_layout_pack (tbl, b[PLUS], 5, 3);
+
+  /* Pack the table into the box and the box into the window. */
+  ml_box_pack (box, tbl);
+  ml_window_pack (w, box);
+
+  /* Save the display widget in the per-session data structure so
+   * that the callback functions can update it.
+   */
+  data->disp = disp;
+  strcpy (data->digits, "0");
+  data->reg = 0;
+  data->op = 0;
+}
+
+static void press_button_N (ml_session, int, void *);
+
+static void
+press_button_0 (ml_session session, void *vp)
+{
+  press_button_N (session, 0, vp);
+}
+
+static void
+press_button_1 (ml_session session, void *vp)
+{
+  press_button_N (session, 1, vp);
+}
+
+static void
+press_button_2 (ml_session session, void *vp)
+{
+  press_button_N (session, 2, vp);
+}
+
+static void
+press_button_3 (ml_session session, void *vp)
+{
+  press_button_N (session, 3, vp);
+}
+
+static void
+press_button_4 (ml_session session, void *vp)
+{
+  press_button_N (session, 4, vp);
+}
+
+static void
+press_button_5 (ml_session session, void *vp)
+{
+  press_button_N (session, 5, vp);
+}
+
+static void
+press_button_6 (ml_session session, void *vp)
+{
+  press_button_N (session, 6, vp);
+}
+
+static void
+press_button_7 (ml_session session, void *vp)
+{
+  press_button_N (session, 7, vp);
+}
+
+static void
+press_button_8 (ml_session session, void *vp)
+{
+  press_button_N (session, 8, vp);
+}
+
+static void
+press_button_9 (ml_session session, void *vp)
+{
+  press_button_N (session, 9, vp);
+}
+
+static void
+press_button_N (ml_session session, int n, void *vp)
+{
+  struct data *data = (struct data *) vp;
+  int len;
+
+  if (strcmp (data->digits, "0") == 0)
+    data->digits[0] = '\0';
+
+  len = strlen (data->digits);
+
+  if ((strchr (data->digits, '.') && len < 11) || len < 10)
+    {
+      data->digits[len] = '0' + n;
+      data->digits[len+1] = '\0';
+      ml_widget_set_property (data->disp, "text", data->digits);
+    }
+}
+
+static void
+press_button_DOT (ml_session session, void *vp)
+{
+  struct data *data = (struct data *) vp;
+  int len = strlen (data->digits);
+
+  if (strchr (data->digits, '.') == 0 && len < 10)
+    {
+      strcat (data->digits, ".");
+      ml_widget_set_property (data->disp, "text", data->digits);
+    }
+}
+
+static void
+press_button_EQUALS (ml_session session, void *vp)
+{
+  struct data *data = (struct data *) vp;
+  double a;
+
+  if (data->op)
+    {
+      sscanf (data->digits, "%lf", &a);
+
+      if (data->op == PLUS)
+       data->reg += a;
+      else if (data->op == MINUS)
+       data->reg -= a;
+      else if (data->op == TIMES)
+       data->reg *= a;
+      else if (data->op == DIVIDE && a != 0)
+       data->reg /= a;
+
+      snprintf (data->digits, 10, "%g", data->reg);
+      ml_widget_set_property (data->disp, "text", data->digits);
+      data->op = 0;
+    }
+}
+
+static void
+press_button_PLUS (ml_session session, void *vp)
+{
+  struct data *data = (struct data *) vp;
+
+  /* Act like we just pressed the EQUALS key. */
+  press_button_EQUALS (session, data);
+
+  sscanf (data->digits, "%lf", &data->reg);
+  strcpy (data->digits, "0");  /* But DON'T update the label yet. */
+  data->op = PLUS;
+}
+
+static void
+press_button_MINUS (ml_session session, void *vp)
+{
+  struct data *data = (struct data *) vp;
+
+  /* Act like we just pressed the EQUALS key. */
+  press_button_EQUALS (session, data);
+
+  sscanf (data->digits, "%lf", &data->reg);
+  strcpy (data->digits, "0");  /* But DON'T update the label yet. */
+  data->op = MINUS;
+}
+
+static void
+press_button_TIMES (ml_session session, void *vp)
+{
+  struct data *data = (struct data *) vp;
+
+  /* Act like we just pressed the EQUALS key. */
+  press_button_EQUALS (session, data);
+
+  sscanf (data->digits, "%lf", &data->reg);
+  strcpy (data->digits, "0");  /* But DON'T update the label yet. */
+  data->op = TIMES;
+}
+
+static void
+press_button_DIVIDE (ml_session session, void *vp)
+{
+  struct data *data = (struct data *) vp;
+
+  /* Act like we just pressed the EQUALS key. */
+  press_button_EQUALS (session, data);
+
+  sscanf (data->digits, "%lf", &data->reg);
+  strcpy (data->digits, "0");  /* But DON'T update the label yet. */
+  data->op = DIVIDE;
+}
+
+static void
+press_button_CLEAR (ml_session session, void *vp)
+{
+  struct data *data = (struct data *) vp;
+
+  strcpy (data->digits, "0");
+  ml_widget_set_property (data->disp, "text", "0");
+}
+
+static void
+press_button_AC (ml_session session, void *vp)
+{
+  struct data *data = (struct data *) vp;
+
+  strcpy (data->digits, "0");
+  data->reg = 0;
+  ml_widget_set_property (data->disp, "text", "0");
+}
diff --git a/examples/03_many_toy_calculators.c b/examples/03_many_toy_calculators.c
new file mode 100644 (file)
index 0000000..4b13927
--- /dev/null
@@ -0,0 +1,68 @@
+/* Toy calculator from example 02 turned into a reusable widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: 03_many_toy_calculators.c,v 1.3 2002/09/07 13:46:58 rich Exp $
+ */
+
+#include <string.h>
+
+#include <pool.h>
+#include <pstring.h>
+#include <pthr_cgi.h>
+
+#include "monolith.h"
+#include "ml_window.h"
+#include "ml_table_layout.h"
+
+#include "toy_calculator.h"
+
+/* Main entry point to the app. */
+static void app_main (ml_session);
+
+int
+handle_request (rws_request rq)
+{
+  return ml_entry_point (rq, app_main);
+}
+
+static void
+app_main (ml_session session)
+{
+  pool pool = ml_session_pool (session);
+  ml_window w;
+  ml_table_layout tbl;
+  toy_calculator calcs[4];
+
+  /* Create the top-level window. */
+  w = new_ml_window (session, pool);
+
+  /* Create a table layout widget to arrange the calculators. */
+  tbl = new_ml_table_layout (pool, 2, 2);
+
+  /* Create the calculators and pack them into the table layout. */
+  calcs[0] = new_toy_calculator (pool, session);
+  ml_table_layout_pack (tbl, calcs[0], 0, 0);
+  calcs[1] = new_toy_calculator (pool, session);
+  ml_table_layout_pack (tbl, calcs[1], 0, 1);
+  calcs[2] = new_toy_calculator (pool, session);
+  ml_table_layout_pack (tbl, calcs[2], 1, 0);
+  calcs[3] = new_toy_calculator (pool, session);
+  ml_table_layout_pack (tbl, calcs[3], 1, 1);
+
+  /* Pack the table into the window. */
+  ml_window_pack (w, tbl);
+}
diff --git a/examples/04_animal_vegetable_mineral.c b/examples/04_animal_vegetable_mineral.c
new file mode 100644 (file)
index 0000000..54bc93b
--- /dev/null
@@ -0,0 +1,532 @@
+/* Animal, vegetable, mineral; or "20 questions"
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: 04_animal_vegetable_mineral.c,v 1.8 2003/02/08 15:53:32 rich Exp $
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include <pool.h>
+#include <pstring.h>
+#include <pthr_cgi.h>
+
+#include "monolith.h"
+#include "ml_window.h"
+#include "ml_dialog.h"
+#include "ml_form.h"
+#include "ml_form_text.h"
+#include "ml_form_textarea.h"
+#include "ml_form_submit.h"
+#include "ml_table_layout.h"
+#include "ml_text_label.h"
+
+/* Main entry point to the app. */
+static void app_main (ml_session);
+
+int
+handle_request (rws_request rq)
+{
+  return ml_entry_point (rq, app_main);
+}
+
+/* This is the location of the questions database. You might want to
+ * put this somewhere more permanent than just in /tmp.
+ */
+#define DATABASE_FILE "/tmp/questions_database"
+
+/* Private per-session data. */
+struct data
+{
+  ml_window win;               /* Top-level window. */
+  ml_dialog dialog;            /* Dialog widget. */
+  ml_form_text input1;         /* Input fields on the final form. */
+  ml_form_textarea input2;
+  ml_form_submit input3, input4;
+  int current_node;            /* The node we are currently displaying. */
+};
+
+static void start_game (ml_session, void *);
+
+static void
+app_main (ml_session session)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data;
+
+  /* Create the per-session data area. */
+  data = pmalloc (pool, sizeof *data);
+
+  /* Create the top-level window. */
+  data->win = new_ml_window (session, pool);
+
+  /* Create the dialog widget. */
+  data->dialog = new_ml_dialog (pool);
+  ml_dialog_set_title (data->dialog, "Animal, Vegetable, Mineral game "
+                                    "(a.k.a. \"20 questions\")");
+
+  /* Start the game. This populates the initial dialog with the first
+   * question.
+   */
+  start_game (session, data);
+
+  /* Pack everything up. */
+  ml_window_pack (data->win, data->dialog);
+}
+
+/* This data is shared between sessions. */
+static pool avm_pool;          /* Pool for global allocations. */
+static vector nodes;           /* List of nodes. */
+static int a, v, m;            /* Index of animal, vegetable and mineral
+                                * nodes in the list of nodes. */
+
+static void avm_init (void) __attribute__((constructor));
+static void avm_stop (void) __attribute__((destructor));
+
+static void pressed_animal (ml_session, void *);
+static void pressed_vegetable (ml_session, void *);
+static void pressed_mineral (ml_session, void *);
+static void pressed_yes (ml_session session, void *);
+static void pressed_no (ml_session session, void *);
+static void update_dialog (ml_session session, struct data *);
+static void finished_game (ml_session session, void *);
+static void unknown_thing (ml_session session, void *);
+static void add_unknown_thing (ml_session session, void *);
+static void save_database (void);
+static int is_vowel (const char *);
+static void remove_newlines (char *);
+
+struct node
+{
+  int is_question;             /* Either a question or an answer node. */
+
+  /* For question nodes, this is the text of the question. For answer
+   * nodes, this is the expected answer word.
+   */
+  const char *text;
+
+  /* For question nodes only, this is the index of the nodes to visit
+   * if the user says 'yes' or 'no' respectively.
+   */
+  int yes, no;
+};
+
+static void
+avm_init ()
+{
+  FILE *fp;
+  struct node node;
+  char buffer[512];
+
+  avm_pool = new_subpool (global_pool);
+  nodes = new_vector (avm_pool, struct node);
+
+  fp = fopen (DATABASE_FILE, "r");
+  if (fp == 0)
+    {
+      /* No initial database. Don't worry, just create one internally. */
+      a = 1;
+      v = 2;
+      m = 3;
+
+      /* Node 0 is a dummy node. */
+      vector_push_back (nodes, node);
+
+      /* Node 1 is the animal node. */
+      node.is_question = 0;
+      node.text = "horse";
+      vector_push_back (nodes, node);
+
+      /* Node 2 is the vegetable node. */
+      node.is_question = 0;
+      node.text = "leek";
+      vector_push_back (nodes, node);
+
+      /* Node 3 is the mineral node. */
+      node.is_question = 0;
+      node.text = "pumice stone";
+      vector_push_back (nodes, node);
+
+      return;
+    }
+
+  while (fgets (buffer, sizeof buffer, fp))
+    {
+      pchomp (buffer);
+
+      if (vector_size (nodes) == 0) /* Node 0 is special. */
+       {
+         if (sscanf (buffer, "q %d %d %d", &a, &v, &m)
+             != 3)
+           {
+             fprintf (stderr, "%s: database is corrupt\n", DATABASE_FILE);
+             abort ();
+           }
+         vector_push_back (nodes, node);
+
+#if 0
+         fprintf (stderr, "a = %d, v = %d, m = %d\n",
+                  a, v, m);
+#endif
+       }
+      else
+       {
+         if (buffer[0] == 'q') /* Question node. */
+           {
+             int n;
+
+             node.is_question = 1;
+
+             if (sscanf (buffer, "q %d %d %n", &node.yes, &node.no, &n) < 2)
+               {
+                 fprintf (stderr, "%s: database is corrupt\n", DATABASE_FILE);
+                 abort ();
+               }
+
+             node.text = pstrdup (avm_pool, &buffer[n]);
+
+             vector_push_back (nodes, node);
+
+#if 0
+             fprintf (stderr, "yes = %d, no = %d, text = %s\n",
+                      node.yes, node.no, node.text);
+#endif
+           }
+         else                  /* Answer node. */
+           {
+             node.is_question = 0;
+
+             node.text = pstrdup (avm_pool, &buffer[2]);
+
+             vector_push_back (nodes, node);
+
+#if 0
+             fprintf (stderr, "text = %s\n",
+                      node.text);
+#endif
+           }
+       }
+    }
+
+#if 0
+  fprintf (stderr, "finished\n");
+#endif
+
+  fclose (fp);
+}
+
+static void
+avm_stop ()
+{
+  delete_pool (avm_pool);
+}
+
+static void
+save_database ()
+{
+  FILE *fp;
+  int i;
+  struct node node;
+
+  fp = fopen (DATABASE_FILE, "w");
+  if (fp == 0)
+    {
+      perror (DATABASE_FILE);
+      return;
+    }
+
+  /* Node 0 is special. */
+  fprintf (fp, "q %d %d %d\n", a, v, m);
+
+  /* Write out the other nodes in order. */
+  for (i = 1; i < vector_size (nodes); ++i)
+    {
+      vector_get (nodes, i, node);
+
+      if (node.is_question)
+       fprintf (fp, "q %d %d %s\n", node.yes, node.no, node.text);
+      else
+       fprintf (fp, "a %s\n", node.text);
+    }
+
+  fclose (fp);
+}
+
+static void
+start_game (ml_session session, void *vp)
+{
+  struct data *data = (struct data *) vp;
+
+  data->current_node = 0;
+
+  /* Ask the initial standard question. */
+  ml_dialog_set_text (data->dialog,
+                     "<p>Think of something, anything.</p>"
+                     "<p>I will ask you questions about it "
+                     "and try to guess what it is.</p>"
+                     "<p>First question: Is it an animal, a vegetable "
+                     "or a mineral?</p>");
+  ml_dialog_clear_buttons (data->dialog);
+  ml_dialog_add_button (data->dialog, "Animal",
+                       pressed_animal, session, data);
+  ml_dialog_add_button (data->dialog, "Vegetable",
+                       pressed_vegetable, session, data);
+  ml_dialog_add_button (data->dialog, "Mineral",
+                       pressed_mineral, session, data);
+}
+
+static void
+pressed_animal (ml_session session, void *vp)
+{
+  struct data *data = (struct data *) vp;
+
+  data->current_node = a;
+  update_dialog (session, data);
+}
+
+static void
+pressed_vegetable (ml_session session, void *vp)
+{
+  struct data *data = (struct data *) vp;
+
+  data->current_node = v;
+  update_dialog (session, data);
+}
+
+static void
+pressed_mineral (ml_session session, void *vp)
+{
+  struct data *data = (struct data *) vp;
+
+  data->current_node = m;
+  update_dialog (session, data);
+}
+
+static void
+pressed_yes (ml_session session, void *vp)
+{
+  struct data *data = (struct data *) vp;
+  struct node node;
+
+  vector_get (nodes, data->current_node, node);
+  data->current_node = node.yes;
+  update_dialog (session, data);
+}
+
+static void
+pressed_no (ml_session session, void *vp)
+{
+  struct data *data = (struct data *) vp;
+  struct node node;
+
+  vector_get (nodes, data->current_node, node);
+  data->current_node = node.no;
+  update_dialog (session, data);
+}
+
+static void
+update_dialog (ml_session session, struct data *data)
+{
+  pool pool = ml_session_pool (session);
+  struct node node;
+
+  /* Fetch the current node. */
+  vector_get (nodes, data->current_node, node);
+
+  if (node.is_question)                /* Question node. */
+    {
+      /* Present the dialog window. */
+      ml_dialog_set_text (data->dialog, node.text);
+
+      /* Update the buttons. */
+      ml_dialog_clear_buttons (data->dialog);
+      ml_dialog_add_button (data->dialog, "Yes",
+                           pressed_yes, session, data);
+      ml_dialog_add_button (data->dialog, "No",
+                           pressed_no, session, data);
+    }
+  else                         /* Answer node. */
+    {
+      /* Present the dialog window. */
+      ml_dialog_set_text (data->dialog,
+                         psprintf (pool,
+                                   "Is it %s %s?",
+                                   (!is_vowel (node.text) ? "a" : "an"),
+                                   node.text));
+
+      /* Update the buttons. */
+      ml_dialog_clear_buttons (data->dialog);
+      ml_dialog_add_button (data->dialog, "Yes",
+                           finished_game, session, data);
+      ml_dialog_add_button (data->dialog, "No",
+                           unknown_thing, session, data);
+    }
+}
+
+static int
+is_vowel (const char *text)
+{
+  while (*text)
+    {
+      if (isalpha ((int) *text))
+       {
+         return *text == 'a' || *text == 'e' || *text == 'i' ||
+           *text == 'o' || *text == 'u' || *text == 'A' || *text == 'E' ||
+           *text == 'I' || *text == 'O' || *text == 'U';
+       }
+
+      text++;
+    }
+  return 0;
+}
+
+static void
+finished_game (ml_session session, void *vp)
+{
+  struct data *data = (struct data *) vp;
+
+  ml_dialog_set_text (data->dialog, "Thanks for playing!");
+  ml_dialog_clear_buttons (data->dialog);
+  ml_dialog_add_button (data->dialog, "Play again",
+                       start_game, session, data);
+}
+
+static void
+unknown_thing (ml_session session, void *vp)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data = (struct data *) vp;
+  ml_form form;
+  ml_table_layout tbl;
+  ml_text_label text1, text2, text3;
+  struct node node;
+
+  vector_get (nodes, data->current_node, node);  /* Fetch the node. */
+
+  /* Generate the form used to ask the user to input a new question. */
+  form = new_ml_form (pool);
+  ml_form_set_callback (form, add_unknown_thing, session, data);
+
+  /* The form contains a table layout ... */
+  tbl = new_ml_table_layout (pool, 6, 2);
+
+  /* Populate the form. */
+  text1 = new_ml_text_label (pool,
+                            "I give up! What was the thing you were "
+                            "thinking of?");
+  ml_table_layout_pack (tbl, text1, 0, 0);
+  ml_table_layout_set_colspan (tbl, 0, 0, 2);
+
+  data->input1 = new_ml_form_text (pool, form);
+  ml_table_layout_pack (tbl, data->input1, 1, 0);
+  ml_table_layout_set_colspan (tbl, 1, 0, 2);
+
+  text2 = new_ml_text_label
+    (pool,
+     psprintf (pool,
+              "Give me a simple yes/no question which I can "
+              "ask people, to distinguish between your thing "
+              "and %s %s.",
+              (!is_vowel (node.text) ? "a" : "an"),
+              node.text));
+  ml_table_layout_pack (tbl, text2, 2, 0);
+  ml_table_layout_set_colspan (tbl, 2, 0, 2);
+
+  data->input2 = new_ml_form_textarea (pool, form, 4, 60);
+  ml_table_layout_pack (tbl, data->input2, 3, 0);
+  ml_table_layout_set_colspan (tbl, 3, 0, 2);
+
+  text3 = new_ml_text_label
+    (pool,
+     "And if I asked the question above of someone, and they "
+     "were thinking of your thing, would they answer Yes or No?");
+  ml_table_layout_pack (tbl, text3, 4, 0);
+  ml_table_layout_set_colspan (tbl, 4, 0, 2);
+
+  data->input3 = new_ml_form_submit (pool, form, "Yes");
+  ml_table_layout_pack (tbl, data->input3, 5, 0);
+
+  data->input4 = new_ml_form_submit (pool, form, "No");
+  ml_table_layout_pack (tbl, data->input4, 5, 1);
+
+  /* Pack. */
+  ml_form_pack (form, tbl);
+  ml_window_pack (data->win, form);
+}
+
+static void
+add_unknown_thing (ml_session session, void *vp)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data = (struct data *) vp;
+  int old_ans, new_ans;
+  struct node old_ans_node, new_q_node, new_ans_node;
+  char *name, *question;
+  int yes_button, no_button;
+
+  vector_get (nodes, data->current_node, old_ans_node); /* Fetch the node. */
+
+  /* Extract the values of the input fields. */
+  name = pstrdup (pool, ml_form_input_get_value (data->input1));
+  question = pstrdup (pool, ml_form_input_get_value (data->input2));
+  yes_button = ml_form_input_get_value (data->input3) ? 1 : 0;
+  no_button = ml_form_input_get_value (data->input4) ? 1 : 0;
+
+  assert ((yes_button || no_button) && ! (yes_button && no_button));
+
+  /* Remove newlines from the fields (replace with whitespace). This is
+   * because the database file cannot handle newlines.
+   */
+  remove_newlines (name);
+  remove_newlines (question);
+
+  /* Update the tree. */
+  old_ans = vector_size (nodes);
+  vector_push_back (nodes, old_ans_node);
+
+  new_ans = vector_size (nodes);
+  assert (new_ans == old_ans + 1);
+  new_ans_node.is_question = 0;
+  new_ans_node.text = pstrdup (avm_pool, name);
+  vector_push_back (nodes, new_ans_node);
+
+  new_q_node.is_question = 1;
+  new_q_node.text = pstrdup (avm_pool, question);
+  new_q_node.yes = yes_button ? new_ans : old_ans;
+  new_q_node.no = no_button ? new_ans : old_ans;
+  vector_replace (nodes, data->current_node, new_q_node);
+
+  save_database ();
+
+  /* Replace the form with the original dialog. */
+  ml_window_pack (data->win, data->dialog);
+
+  /* Restart the game. */
+  start_game (session, data);
+}
+
+static void
+remove_newlines (char *str)
+{
+  while (*str)
+    {
+      if (*str == '\n') *str = ' ';
+      str++;
+    }
+}
diff --git a/examples/05_popup_windows_and_frames.c b/examples/05_popup_windows_and_frames.c
new file mode 100644 (file)
index 0000000..b61a68a
--- /dev/null
@@ -0,0 +1,309 @@
+/* Demonstrate pop-up windows and frames.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: 05_popup_windows_and_frames.c,v 1.6 2002/10/30 21:03:00 rich Exp $
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+#include <pool.h>
+#include <pstring.h>
+#include <pthr_cgi.h>
+
+#include "monolith.h"
+#include "ml_window.h"
+#include "ml_button.h"
+#include "ml_table_layout.h"
+#include "ml_text_label.h"
+#include "ml_form.h"
+#include "ml_form_text.h"
+#include "ml_form_submit.h"
+
+/* Main entry point to the app. */
+static void app_main (ml_session);
+
+int
+handle_request (rws_request rq)
+{
+  return ml_entry_point (rq, app_main);
+}
+
+/* Private per-session data. */
+struct data
+{
+  /* Main window. */
+  ml_window main_win;
+  ml_table_layout main_tbl;
+  ml_text_label main_text;
+  ml_button main_b[3];
+
+  /* Area-of-circle window. */
+  ml_window circle_win;
+  ml_table_layout circle_tbl;
+  ml_text_label circle_text1, circle_text2, circle_ans;
+  ml_form circle_form;
+  ml_form_text circle_input;
+  ml_form_submit circle_submit;
+
+  /* Volume-of-cone window. */
+  ml_window cone_win;
+  ml_table_layout cone_tbl;
+  ml_text_label cone_text1, cone_text2, cone_text3, cone_ans;
+  ml_form cone_form;
+  ml_form_text cone_input1, cone_input2;
+  ml_form_submit cone_submit;
+
+  /* Frames window. */
+  ml_window frames_win;
+  ml_window frames_tl_win;
+  ml_window frames_bl_win;
+  ml_window frames_tr_win;
+  ml_window frames_br_win;
+  ml_text_label frames_tl_text;
+  ml_text_label frames_bl_text;
+  ml_text_label frames_tr_text;
+  ml_text_label frames_br_text;
+};
+
+static void circle (ml_session session, void *vp);
+static void circle_calculate (ml_session session, void *vp);
+static void cone (ml_session session, void *vp);
+static void cone_calculate (ml_session session, void *vp);
+static void frames (ml_session session, void *vp);
+static void tl_frame (ml_session session, void *vp);
+static void bl_frame (ml_session session, void *vp);
+static void tr_frame (ml_session session, void *vp);
+static void br_frame (ml_session session, void *vp);
+
+static void
+app_main (ml_session session)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data;
+
+  /* Create the per-session data area. */
+  data = pmalloc (pool, sizeof *data);
+
+  /* Create the top-level window. */
+  data->main_win = new_ml_window (session, pool);
+  data->main_tbl = new_ml_table_layout (pool, 2, 3);
+  data->main_text = new_ml_text_label (pool, "Select a demo");
+
+  /* Create some buttons to launch the different windows. */
+  data->main_b[0] = new_ml_button (pool, "Area of a circle");
+  ml_button_set_popup (data->main_b[0], "popupdemo_circle");
+  ml_button_set_popup_size (data->main_b[0], 300, 200);
+  ml_button_set_callback (data->main_b[0], circle, session, data);
+  data->main_b[1] = new_ml_button (pool, "Volume of a cone");
+  ml_button_set_popup (data->main_b[1], "popupdemo_cone");
+  ml_button_set_popup_size (data->main_b[1], 300, 200);
+  ml_button_set_callback (data->main_b[1], cone, session, data);
+  data->main_b[2] = new_ml_button (pool, "Frames demo");
+  ml_button_set_popup (data->main_b[2], "popupdemo_frames");
+  ml_button_set_popup_size (data->main_b[2], 640, 480);
+  ml_button_set_callback (data->main_b[2], frames, session, data);
+
+  /* Pack everything up. */
+  ml_table_layout_pack (data->main_tbl, data->main_text, 0, 0);
+  ml_table_layout_set_colspan (data->main_tbl, 0, 0, 3);
+  ml_table_layout_set_align (data->main_tbl, 0, 0, "center");
+  ml_table_layout_pack (data->main_tbl, data->main_b[0], 1, 0);
+  ml_table_layout_pack (data->main_tbl, data->main_b[1], 1, 1);
+  ml_table_layout_pack (data->main_tbl, data->main_b[2], 1, 2);
+  ml_window_pack (data->main_win, data->main_tbl);
+}
+
+static void
+circle (ml_session session, void *vp)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data = (struct data *) vp;
+
+  /* Top-level window. */
+  data->circle_win = new_ml_window (session, pool);
+
+  /* Construct the form. */
+  data->circle_form = new_ml_form (pool);
+  data->circle_tbl = new_ml_table_layout (pool, 3, 2);
+  data->circle_text1 = new_ml_text_label (pool, "Radius:");
+  ml_table_layout_pack (data->circle_tbl, data->circle_text1, 0, 0);
+  data->circle_input = new_ml_form_text (pool, data->circle_form);
+  ml_table_layout_pack (data->circle_tbl, data->circle_input, 0, 1);
+  data->circle_submit = new_ml_form_submit (pool, data->circle_form,
+                                           "Calculate");
+  ml_table_layout_pack (data->circle_tbl, data->circle_submit, 1, 1);
+  data->circle_text2 = new_ml_text_label (pool, "Answer:");
+  ml_table_layout_pack (data->circle_tbl, data->circle_text2, 2, 0);
+  data->circle_ans = new_ml_text_label (pool, "");
+  ml_table_layout_pack (data->circle_tbl, data->circle_ans, 2, 1);
+
+  ml_form_pack (data->circle_form, data->circle_tbl);
+  ml_window_pack (data->circle_win, data->circle_form);
+
+  /* Set the callback for the form. */
+  ml_form_set_callback (data->circle_form, circle_calculate, session, data);
+}
+
+static void
+circle_calculate (ml_session session, void *vp)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data = (struct data *) vp;
+  const char *r;
+  double radius, area;
+
+  r = ml_form_input_get_value (data->circle_input);
+  if (r && sscanf (r, "%lf", &radius) == 1)
+    {
+      area = M_PI * radius * radius;
+      ml_widget_set_property (data->circle_ans, "text",
+                             psprintf (pool, "%g", area));
+    }
+  else
+    {
+      ml_widget_set_property (data->circle_ans, "text", "Not a number!");
+    }
+}
+
+static void
+cone (ml_session session, void *vp)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data = (struct data *) vp;
+
+  /* Top-level window. */
+  data->cone_win = new_ml_window (session, pool);
+
+  /* Construct the form. */
+  data->cone_form = new_ml_form (pool);
+  data->cone_tbl = new_ml_table_layout (pool, 4, 2);
+  data->cone_text1 = new_ml_text_label (pool, "Radius:");
+  ml_table_layout_pack (data->cone_tbl, data->cone_text1, 0, 0);
+  data->cone_input1 = new_ml_form_text (pool, data->cone_form);
+  ml_table_layout_pack (data->cone_tbl, data->cone_input1, 0, 1);
+  data->cone_text3 = new_ml_text_label (pool, "Height:");
+  ml_table_layout_pack (data->cone_tbl, data->cone_text3, 1, 0);
+  data->cone_input2 = new_ml_form_text (pool, data->cone_form);
+  ml_table_layout_pack (data->cone_tbl, data->cone_input2, 1, 1);
+  data->cone_submit = new_ml_form_submit (pool, data->cone_form,
+                                         "Calculate");
+  ml_table_layout_pack (data->cone_tbl, data->cone_submit, 2, 1);
+  data->cone_text2 = new_ml_text_label (pool, "Answer:");
+  ml_table_layout_pack (data->cone_tbl, data->cone_text2, 3, 0);
+  data->cone_ans = new_ml_text_label (pool, "");
+  ml_table_layout_pack (data->cone_tbl, data->cone_ans, 3, 1);
+
+  ml_form_pack (data->cone_form, data->cone_tbl);
+  ml_window_pack (data->cone_win, data->cone_form);
+
+  /* Set the callback for the form. */
+  ml_form_set_callback (data->cone_form, cone_calculate, session, data);
+}
+
+static void
+cone_calculate (ml_session session, void *vp)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data = (struct data *) vp;
+  const char *r, *h;
+  double radius, height, vol;
+
+  r = ml_form_input_get_value (data->cone_input1);
+  h = ml_form_input_get_value (data->cone_input2);
+  if (r && sscanf (r, "%lf", &radius) == 1 &&
+      h && sscanf (h, "%lf", &height) == 1)
+    {
+      vol = M_PI * radius * radius * height / 3.0;
+      ml_widget_set_property (data->cone_ans, "text",
+                             psprintf (pool, "%g", vol));
+    }
+  else
+    {
+      ml_widget_set_property (data->cone_ans, "text", "Not a number!");
+    }
+}
+
+static void
+frames (ml_session session, void *vp)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data = (struct data *) vp;
+  vector fs;
+  struct ml_frame_description f;
+
+  /* Create the top-level frameset. */
+  fs = new_vector (pool, struct ml_frame_description);
+
+  f.fn = tl_frame; f.data = data;
+  vector_push_back (fs, f);
+
+  f.fn = bl_frame; f.data = data;
+  vector_push_back (fs, f);
+
+  f.fn = tr_frame; f.data = data;
+  vector_push_back (fs, f);
+
+  f.fn = br_frame; f.data = data;
+  vector_push_back (fs, f);
+
+  data->frames_win = new_ml_frameset (session, pool, "60%,40%", "34%,66%", fs);
+}
+
+static void
+tl_frame (ml_session session, void *vp)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data = (struct data *) vp;
+
+  data->frames_tl_win = new_ml_window (session, pool);
+  data->frames_tl_text = new_ml_text_label (pool, "Top left frame");
+  ml_window_pack (data->frames_tl_win, data->frames_tl_text);
+}
+
+static void
+bl_frame (ml_session session, void *vp)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data = (struct data *) vp;
+
+  data->frames_bl_win = new_ml_window (session, pool);
+  data->frames_bl_text = new_ml_text_label (pool, "Bottom left frame");
+  ml_window_pack (data->frames_bl_win, data->frames_bl_text);
+}
+
+static void
+tr_frame (ml_session session, void *vp)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data = (struct data *) vp;
+
+  data->frames_tr_win = new_ml_window (session, pool);
+  data->frames_tr_text = new_ml_text_label (pool, "Top right frame");
+  ml_window_pack (data->frames_tr_win, data->frames_tr_text);
+}
+
+static void
+br_frame (ml_session session, void *vp)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data = (struct data *) vp;
+
+  data->frames_br_win = new_ml_window (session, pool);
+  data->frames_br_text = new_ml_text_label (pool, "Bottom right frame");
+  ml_window_pack (data->frames_br_win, data->frames_br_text);
+}
diff --git a/examples/06_big_form.c b/examples/06_big_form.c
new file mode 100644 (file)
index 0000000..96dd997
--- /dev/null
@@ -0,0 +1,277 @@
+/* Big, complicated form.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: 06_big_form.c,v 1.4 2002/11/02 18:53:46 rich Exp $
+ */
+
+#include <string.h>
+
+#include <pool.h>
+#include <pstring.h>
+#include <pthr_cgi.h>
+
+#include "monolith.h"
+#include "ml_window.h"
+#include "ml_text_label.h"
+#include "ml_button.h"
+#include "ml_table_layout.h"
+#include "ml_flow_layout.h"
+#include "ml_form.h"
+#include "ml_form_submit.h"
+#include "ml_form_text.h"
+#include "ml_form_textarea.h"
+#include "ml_form_password.h"
+#include "ml_form_select.h"
+#include "ml_form_radio_group.h"
+#include "ml_form_radio.h"
+#include "ml_form_checkbox.h"
+#include "ml_form_textarea.h"
+
+/*----- The following standard boilerplate code must appear -----*/
+
+/* Main entry point to the app. */
+static void app_main (ml_session);
+
+int
+handle_request (rws_request rq)
+{
+  return ml_entry_point (rq, app_main);
+}
+
+/*----- End of standard boilerplate code -----*/
+
+/* Private per-session data. */
+struct data
+{
+  ml_window win;
+
+  /* The form input fields themselves. */
+  ml_form_text familyname, givenname;
+  ml_form_password password;
+  ml_form_select dd, mm, yyyy; /* Date of birth. */
+  ml_form_radio_group gender;
+  ml_form_radio m, f;          /* Gender. */
+  ml_form_checkbox eating, drinking, sleeping; /* Interests */
+  /*ml_form_file photo;            File upload, not yet implemented. */
+  ml_form_select dept;
+  ml_form_textarea comments;
+};
+
+static void create_form (ml_session session, void *vp);
+static void submit_form (ml_session session, void *vp);
+
+static void
+app_main (ml_session session)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data;
+
+  /* Create the private, per-session data area and save it in the
+   * session object.
+   */
+  data = pmalloc (pool, sizeof *data);
+
+  /* Create the top-level window. */
+  data->win = new_ml_window (session, pool);
+
+  create_form (session, data);
+}
+
+static void
+create_form (ml_session session, void *vp)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data = (struct data *) vp;
+  ml_form form;
+  ml_table_layout tbl;
+  ml_text_label text;
+  ml_form_submit submit;
+  ml_flow_layout flow;
+  int i;
+
+  /* Create the form. */
+  form = new_ml_form (pool);
+  ml_form_set_callback (form, submit_form, session, data);
+
+  /* Create the table. */
+  tbl = new_ml_table_layout (pool, 10, 2);
+
+  /* Create the contents of the form. */
+  text = new_ml_text_label (pool, "Family name / surname");
+  ml_table_layout_pack (tbl, text, 0, 0);
+  data->familyname = new_ml_form_text (pool, form);
+  ml_table_layout_pack (tbl, data->familyname, 0, 1);
+
+  text = new_ml_text_label (pool, "Given name(s) / forename(s)");
+  ml_table_layout_pack (tbl, text, 1, 0);
+  data->givenname = new_ml_form_text (pool, form);
+  ml_table_layout_pack (tbl, data->givenname, 1, 1);
+
+  text = new_ml_text_label (pool, "Password");
+  ml_table_layout_pack (tbl, text, 2, 0);
+  data->password = new_ml_form_password (pool, form);
+  ml_table_layout_pack (tbl, data->password, 2, 1);
+
+  text = new_ml_text_label (pool, "Date of birth");
+  ml_table_layout_pack (tbl, text, 3, 0);
+  flow = new_ml_flow_layout (pool);
+  data->dd = new_ml_form_select (pool, form);
+  for (i = 1; i <= 31; ++i)
+    ml_form_select_push_back (data->dd, psprintf (pool, "%02d", i));
+  data->mm = new_ml_form_select (pool, form);
+  ml_form_select_push_back (data->mm, "Jan");
+  ml_form_select_push_back (data->mm, "Feb");
+  ml_form_select_push_back (data->mm, "Mar");
+  ml_form_select_push_back (data->mm, "Apr");
+  ml_form_select_push_back (data->mm, "May");
+  ml_form_select_push_back (data->mm, "Jun");
+  ml_form_select_push_back (data->mm, "Jul");
+  ml_form_select_push_back (data->mm, "Aug");
+  ml_form_select_push_back (data->mm, "Sep");
+  ml_form_select_push_back (data->mm, "Oct");
+  ml_form_select_push_back (data->mm, "Nov");
+  ml_form_select_push_back (data->mm, "Dec");
+  data->yyyy = new_ml_form_select (pool, form);
+  for (i = 1900; i <= 2002; ++i)
+    ml_form_select_push_back (data->yyyy, psprintf (pool, "%04d", i));
+
+  ml_flow_layout_pack (flow, data->dd);
+  ml_flow_layout_pack (flow, data->mm);
+  ml_flow_layout_pack (flow, data->yyyy);
+
+  ml_table_layout_pack (tbl, flow, 3, 1);
+
+  text = new_ml_text_label (pool, "Gender");
+  ml_table_layout_pack (tbl, text, 4, 0);
+  data->gender = new_ml_form_radio_group (pool, form);
+
+  flow = new_ml_flow_layout (pool);
+  data->m = new_ml_form_radio (pool, data->gender, "m");
+  ml_form_radio_set_checked (data->m, 1);
+  ml_flow_layout_pack (flow, data->m);
+  text = new_ml_text_label (pool, "Male");
+  ml_flow_layout_pack (flow, text);
+  data->f = new_ml_form_radio (pool, data->gender, "f");
+  ml_flow_layout_pack (flow, data->f);
+  text = new_ml_text_label (pool, "Female");
+  ml_flow_layout_pack (flow, text);
+
+  ml_form_radio_group_pack (data->gender, flow);
+  ml_table_layout_pack (tbl, data->gender, 4, 1);
+
+  text = new_ml_text_label (pool, "Interests");
+  ml_table_layout_pack (tbl, text, 5, 0);
+
+  flow = new_ml_flow_layout (pool);
+  data->eating = new_ml_form_checkbox (pool, form);
+  ml_flow_layout_pack (flow, data->eating);
+  text = new_ml_text_label (pool, "Eating");
+  ml_flow_layout_pack (flow, text);
+  data->drinking = new_ml_form_checkbox (pool, form);
+  ml_flow_layout_pack (flow, data->drinking);
+  text = new_ml_text_label (pool, "Drinking");
+  ml_flow_layout_pack (flow, text);
+  data->sleeping = new_ml_form_checkbox (pool, form);
+  ml_flow_layout_pack (flow, data->sleeping);
+  text = new_ml_text_label (pool, "Sleeping");
+  ml_flow_layout_pack (flow, text);
+
+  ml_table_layout_pack (tbl, flow, 5, 1);
+
+  text = new_ml_text_label (pool, "Department");
+  ml_table_layout_pack (tbl, text, 6, 0);
+  data->dept = new_ml_form_select (pool, form);
+  ml_widget_set_property (data->dept, "form.select.size", 4);
+  ml_widget_set_property (data->dept, "form.select.multiple", 1);
+  ml_form_select_push_back (data->dept, "Accounting");
+  ml_form_select_push_back (data->dept, "Development");
+  ml_form_select_push_back (data->dept, "Marketing");
+  ml_form_select_push_back (data->dept, "Personnel");
+  ml_form_select_push_back (data->dept, "Support");
+  ml_form_select_push_back (data->dept, "Systems");
+  ml_table_layout_pack (tbl, data->dept, 6, 1);
+
+  text = new_ml_text_label (pool, "Comments");
+  ml_table_layout_pack (tbl, text, 7, 0);
+  data->comments = new_ml_form_textarea (pool, form, 5, 50);
+  ml_table_layout_pack (tbl, data->comments, 7, 1);
+
+  text = new_ml_text_label (pool, "Photo");
+  ml_table_layout_pack (tbl, text, 8, 0);
+  text = new_ml_text_label (pool, "[not implemented]");
+  ml_table_layout_pack (tbl, text, 8, 1);
+
+  /* Submit button. */
+  submit = new_ml_form_submit (pool, form, "Submit");
+  ml_table_layout_pack (tbl, submit, 9, 1);
+
+  /* Pack it all up. */
+  ml_form_pack (form, tbl);
+  ml_window_pack (data->win, form);
+}
+
+static void
+submit_form (ml_session session, void *vp)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data = (struct data *) vp;
+  ml_text_label text;
+  ml_table_layout tbl;
+  ml_button button;
+  const char *str;
+
+  str = psprintf
+    (pool,
+     "Form submitted.\n"
+     "\n"
+     "Family name: %s\n"
+     "Given name: %s\n"
+     "Password: %s\n"
+     "Date of birth: dd = %d, mm = %d, yyyy = %d\n"
+     "Gender: %s\n"
+     "   M is checked: %d  F is checked: %d\n"
+     "Interests: Eating = %d, Drinking = %d, Sleeping = %d\n"
+     "Dept fields checked: [ %s ]\n"
+     "Comments:\n"
+     "--start--\n"
+     "%s\n"
+     "--end--\n",
+     ml_form_input_get_value (data->familyname),
+     ml_form_input_get_value (data->givenname),
+     ml_form_input_get_value (data->password),
+     1 + ml_form_select_get_selection (data->dd),
+     1 + ml_form_select_get_selection (data->mm),
+     1900 + ml_form_select_get_selection (data->yyyy),
+     ml_form_input_get_value (data->gender),
+     ml_form_radio_get_checked (data->m),
+     ml_form_radio_get_checked (data->f),
+     ml_form_input_get_value (data->eating) ? 1 : 0,
+     ml_form_input_get_value (data->drinking) ? 1 : 0,
+     ml_form_input_get_value (data->sleeping) ? 1 : 0,
+     pjoin (pool,
+           pvitostr (pool, ml_form_select_get_selections (data->dept)), ", "),
+     ml_form_input_get_value (data->comments));
+
+  tbl = new_ml_table_layout (pool, 2, 1);
+  text = new_ml_text_label (pool, str);
+  ml_table_layout_pack (tbl, text, 0, 0);
+  button = new_ml_button (pool, "Back to form");
+  ml_button_set_callback (button, create_form, session, data);
+  ml_table_layout_pack (tbl, button, 1, 0);
+
+  ml_window_pack (data->win, tbl);
+}
diff --git a/examples/07_toggle_buttons.c b/examples/07_toggle_buttons.c
new file mode 100644 (file)
index 0000000..20094d4
--- /dev/null
@@ -0,0 +1,127 @@
+/* Toggle buttons.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: 07_toggle_buttons.c,v 1.2 2002/10/30 21:03:01 rich Exp $
+ */
+
+#include <string.h>
+
+#include <pool.h>
+#include <pstring.h>
+#include <pthr_cgi.h>
+
+#include "monolith.h"
+#include "ml_window.h"
+#include "ml_toggle_button.h"
+#include "ml_table_layout.h"
+#include "ml_text_label.h"
+
+/*----- The following standard boilerplate code must appear -----*/
+
+/* Main entry point to the app. */
+static void app_main (ml_session);
+
+int
+handle_request (rws_request rq)
+{
+  return ml_entry_point (rq, app_main);
+}
+
+/*----- End of standard boilerplate code -----*/
+
+/* Private per-session data. */
+struct data
+{
+  /* The toggle buttons. */
+  ml_toggle_button b[4];
+
+  ml_text_label text;
+};
+
+static void update_text (ml_session, void *);
+
+static void
+app_main (ml_session session)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data;
+  ml_window win;
+  ml_table_layout tbl;
+
+  /* Create the private, per-session data area and save it in the
+   * session object.
+   */
+  data = pmalloc (pool, sizeof *data);
+
+  /* Create the top-level window. */
+  win = new_ml_window (session, pool);
+
+  /* Create the table layout. */
+  tbl = new_ml_table_layout (pool, 2, 4);
+
+  /* Create the toggle buttons. */
+  data->b[0] = new_ml_toggle_button (pool, session, "A");
+  data->b[1] = new_ml_toggle_button (pool, session, "B");
+  data->b[2] = new_ml_toggle_button (pool, session, "C");
+  data->b[3] = new_ml_toggle_button (pool, session, "D");
+
+  /* Create the status label. */
+  data->text = new_ml_text_label (pool, "(press a button)");
+
+  /* Set the callback on the buttons to a function which will
+   * update the label.
+   */
+  ml_toggle_button_set_callback (data->b[0], update_text, session, data);
+  ml_toggle_button_set_callback (data->b[1], update_text, session, data);
+  ml_toggle_button_set_callback (data->b[2], update_text, session, data);
+  ml_toggle_button_set_callback (data->b[3], update_text, session, data);
+
+  /* Pack everything together. */
+  ml_table_layout_pack (tbl, data->b[0], 0, 0);
+  ml_table_layout_pack (tbl, data->b[1], 0, 1);
+  ml_table_layout_pack (tbl, data->b[2], 0, 2);
+  ml_table_layout_pack (tbl, data->b[3], 0, 3);
+  ml_table_layout_pack (tbl, data->text, 1, 0);
+  ml_table_layout_set_colspan (tbl, 1, 0, 4);
+  ml_window_pack (win, tbl);
+}
+
+static void
+update_text (ml_session session, void *vp)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data = (struct data *) vp;
+  int a, b, c, d;
+
+  /* Get the state of the four buttons. */
+  a = ml_toggle_button_get_state (data->b[0]);
+  b = ml_toggle_button_get_state (data->b[1]);
+  c = ml_toggle_button_get_state (data->b[2]);
+  d = ml_toggle_button_get_state (data->b[3]);
+
+  ml_widget_set_property
+    (data->text, "text",
+     psprintf (pool,
+              "Button A: %s\n"
+              "Button B: %s\n"
+              "Button C: %s\n"
+              "Button D: %s\n",
+              a ? "pressed" : "not pressed",
+              b ? "pressed" : "not pressed",
+              c ? "pressed" : "not pressed",
+              d ? "pressed" : "not pressed"));
+}
diff --git a/examples/08_menus.c b/examples/08_menus.c
new file mode 100644 (file)
index 0000000..f690337
--- /dev/null
@@ -0,0 +1,167 @@
+/* Menus.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: 08_menus.c,v 1.1 2002/11/30 15:55:46 rich Exp $
+ */
+
+#include <string.h>
+
+#include <pool.h>
+#include <pstring.h>
+#include <pthr_cgi.h>
+
+#include "monolith.h"
+#include "ml_window.h"
+#include "ml_menu.h"
+#include "ml_text_label.h"
+
+/*----- The following standard boilerplate code must appear -----*/
+
+/* Main entry point to the app. */
+static void app_main (ml_session);
+
+int
+handle_request (rws_request rq)
+{
+  return ml_entry_point (rq, app_main);
+}
+
+/*----- End of standard boilerplate code -----*/
+
+/* Private per-session data. */
+struct data
+{
+  ml_window win;               /* Top-level window. */
+  ml_menubar menubar;          /* Menubar along top of window. */
+  ml_text_label text;          /* Label displayed in the main window. */
+};
+
+static void set_red (ml_session session, void *vdata);
+static void set_green (ml_session session, void *vdata);
+static void set_blue (ml_session session, void *vdata);
+static void set_black (ml_session session, void *vdata);
+static void set_small (ml_session session, void *vdata);
+static void set_medium (ml_session session, void *vdata);
+static void set_large (ml_session session, void *vdata);
+
+static void
+app_main (ml_session session)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data;
+  ml_menu menu;
+
+  /* Create the private, per-session data area and save it in the
+   * session object.
+   */
+  data = pmalloc (pool, sizeof *data);
+
+  /* Create the top-level window. */
+  data->win = new_ml_window (session, pool);
+
+  /* Create the menubar. */
+  data->menubar = new_ml_menubar (pool);
+
+  /* Create the "Colours" menu. */
+  menu = new_ml_menu (pool);
+  ml_menu_push_back_button (menu, "Red", set_red, session, data);
+  ml_menu_push_back_button (menu, "Green", set_green, session, data);
+  ml_menu_push_back_button (menu, "Blue", set_blue, session, data);
+  ml_menu_push_back_button (menu, "Black", set_black, session, data);
+
+  /* Pack the "Colours" menu. */
+  ml_menubar_push_back (data->menubar, "Colours", menu);
+
+  /* Create the "Sizes" menu. */
+  menu = new_ml_menu (pool);
+  ml_menu_push_back_button (menu, "Small", set_small, session, data);
+  ml_menu_push_back_button (menu, "Medium", set_medium, session, data);
+  ml_menu_push_back_button (menu, "Large", set_large, session, data);
+
+  /* Pack the "Sizes" menu. */
+  ml_menubar_push_back (data->menubar, "Sizes", menu);
+
+  /* Create the text which appears on the page. (C) Eric Meyer. */
+  data->text = new_ml_text_label
+    (pool,
+     "\n"
+     "In other CSS-aware browsers, like Internet Explorer or Opera, you'll see the toplevel links, and they'll work just fine. The popout menus won't work, that's all. Of course, this means that the browser is downloading the contents of the complete menu, but only able to display a small part of what it downloaded. This is in some respects unfortunate, but at least the basic menu will still function. And the amount of markup involved is probably a lot fewer characters than a similar Javascript-driven menu intended to serve a similar purpose.\n\n"
+     "(And if you're grumbling that you don't see why anyone would deploy a menu system that only worked in one browser, then you should see how many Javascript menus I deal with that don't work in more than one or two browsers-- usually because they're doing browser sniffing as if this were still 1998. But I digress.)\n\n"
+     "If the browser is too old to understand CSS, or doesn't support CSS because (for example) it's a text-mode browser like Lynx, then the user will get the entire menu as a set of nested lists. No problem.\n\n");
+
+  /* Put the text into the menubar widget, so it appears. */
+  ml_menubar_pack (data->menubar, data->text);
+
+  /* Pack the menubar inside the window. */
+  ml_window_pack (data->win, data->menubar);
+}
+
+static void
+set_red (ml_session session, void *vdata)
+{
+  struct data *data = (struct data *) vdata;
+
+  ml_widget_set_property (data->text, "color", "red");
+}
+
+static void
+set_green (ml_session session, void *vdata)
+{
+  struct data *data = (struct data *) vdata;
+
+  ml_widget_set_property (data->text, "color", "green");
+}
+
+static void
+set_blue (ml_session session, void *vdata)
+{
+  struct data *data = (struct data *) vdata;
+
+  ml_widget_set_property (data->text, "color", "blue");
+}
+
+static void
+set_black (ml_session session, void *vdata)
+{
+  struct data *data = (struct data *) vdata;
+
+  ml_widget_set_property (data->text, "color", "black");
+}
+
+static void
+set_small (ml_session session, void *vdata)
+{
+  struct data *data = (struct data *) vdata;
+
+  ml_widget_set_property (data->text, "font.size", "small");
+}
+
+static void
+set_medium (ml_session session, void *vdata)
+{
+  struct data *data = (struct data *) vdata;
+
+  ml_widget_set_property (data->text, "font.size", "medium");
+}
+
+static void
+set_large (ml_session session, void *vdata)
+{
+  struct data *data = (struct data *) vdata;
+
+  ml_widget_set_property (data->text, "font.size", "large");
+}
diff --git a/examples/expenses.c b/examples/expenses.c
new file mode 100644 (file)
index 0000000..72eb252
--- /dev/null
@@ -0,0 +1,71 @@
+/* Simple interface for expenses. */
+
+#include <pool.h>
+#include <vector.h>
+
+#include <monolith.h>
+#include <ml_flow_layout.h>
+#include <ml_vertical_layout.h>
+#include <ml_button.h>
+
+#include "expenses_widget.h"
+
+static void app_main (ml_session session);
+
+struct data
+{
+  vector expenses;             /* The data model. */
+};
+
+/*----- The following standard boilerplate code must appear -----*/
+
+/* Main entry point to the app. */
+static void app_main (ml_session);
+
+int
+handle_request (rws_request rq)
+{
+  return ml_entry_point (rq, app_main);
+}
+
+/*----- End of standard boilerplate code -----*/
+
+static void
+app_main (ml_session session)
+{
+  pool pool = ml_session_pool (session);
+  struct data *data = pmalloc (pool, sizeof *data);
+  ml_window win;
+  expenses_widget ew;
+  ml_flow_layout buttons;
+  ml_vertical_layout tbl;
+  ml_button print, email;
+
+  /* Create the window. */
+  win = new_ml_window (session, pool);
+  ml_session_set_main_window (session, win);
+
+  /* Create the expenses data model - just a vector of expense row
+   * structures.
+   */
+  data->expenses = new_vector (pool, struct expense_row);
+
+  /* Create the expenses widget. */
+  ew = new_expenses_widget (pool, session, data->expenses);
+
+  /* Pack it. */
+  tbl = new_ml_vertical_layout (pool);
+  ml_vertical_layout_pack (tbl, ew);
+
+  /* Create the action button bar. */
+  buttons = new_ml_flow_layout (pool);
+  print = new_ml_button (pool, "Print");
+  ml_flow_layout_pack (buttons, print);
+  email = new_ml_button (pool, "Email");
+  ml_flow_layout_pack (buttons, email);
+
+  ml_vertical_layout_pack (tbl, buttons);
+
+  /* Pack into window. */
+  ml_window_pack (win, tbl);
+}
diff --git a/examples/expenses_widget.c b/examples/expenses_widget.c
new file mode 100644 (file)
index 0000000..24c1659
--- /dev/null
@@ -0,0 +1,74 @@
+/* Expenses widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ */
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <vector.h>
+#include <pthr_iolib.h>
+
+#include <monolith.h>
+#include <ml_widget.h>
+#include <ml_smarttext.h>
+#include <ml_button.h>
+
+#include "expenses_widget.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations expenses_widget_ops =
+  {
+    repaint: repaint
+  };
+
+struct expenses_widget
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_session session;          /* Session. */
+  vector expenses;             /* Underlying data model (vector of
+                                * row structures).
+                                */
+  expenses_add_row add;                /* Add row. */
+};
+
+/* List of expense types. */
+const char *expense_types[] = {
+  "Travel and transport",
+  "Employee morale",
+  "Logding and meals",
+  "Parking and motor",
+  "Entertaining customers",
+  "Office supplies",
+  "Phone",
+  "Other (specify)"
+};
+int nr_expense_types = sizeof (expense_types) / sizeof (expense_types[0]);
+
+expenses_widget
+new_expenses_widget (pool pool, ml_session session, vector expenses)
+{
+  expenses_widget w = pmalloc (pool, sizeof *w);
+
+  w->ops = &expenses_widget_ops;
+  w->pool = pool;
+  w->expenses = expenses;
+
+  /* Create the add row. */
+  w->add = new_expenses_add_row (pool, session, expenses);
+
+  return w;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  //expenses_widget w = (expenses_widget) vw;
+
+  io_fputs ("expenses widget", io);
+}
diff --git a/examples/expenses_widget.h b/examples/expenses_widget.h
new file mode 100644 (file)
index 0000000..d6a90ec
--- /dev/null
@@ -0,0 +1,29 @@
+/* Expenses widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ */
+
+#ifndef EXPENSES_WIDGET_H
+#define EXPENSES_WIDGET_H
+
+#include <monolith.h>
+
+struct expenses_widget;
+typedef struct expenses_widget *expenses_widget;
+
+/* The "data model" is simply a vector of these structures. */
+struct expense_row
+{
+  int type;                    /* Index into expense_types table. */
+  const char *detail;          /* Details of the expense. */
+  int amount;                  /* Amount in PENCE/CENTS. */
+};
+
+extern const char *expense_types[];
+extern int nr_expense_types;
+
+/* Function: new_expenses_widget - expenses widget
+ *
+ */
+extern expenses_widget new_expenses_widget (pool pool, ml_session session, vector expenses);
+
+#endif /* EXPENSES_WIDGET_H */
diff --git a/examples/questions_database b/examples/questions_database
new file mode 100644 (file)
index 0000000..6369ff0
--- /dev/null
@@ -0,0 +1,10 @@
+q 1 2 3
+q 4 5 Does it have four legs?
+a leek
+a pumice stone
+q 7 6 Does it have black and white stripes across its back?
+a human
+q 8 9 Do people ride on it?
+a zebra
+a horse
+a mouse
diff --git a/examples/toy_calculator.c b/examples/toy_calculator.c
new file mode 100644 (file)
index 0000000..425ddcf
--- /dev/null
@@ -0,0 +1,431 @@
+/* Toy calculator from example 02 turned into a reusable widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: toy_calculator.c,v 1.3 2002/11/07 10:49:01 rich Exp $
+ */
+
+#include <string.h>
+
+#include <pool.h>
+#include <pstring.h>
+#include <pthr_cgi.h>
+
+#include "monolith.h"
+#include "ml_window.h"
+#include "ml_table_layout.h"
+#include "ml_text_label.h"
+#include "ml_box.h"
+#include "ml_button.h"
+#include "ml_widget.h"
+
+#include "toy_calculator.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations toy_calculator_ops =
+  {
+    repaint: repaint
+  };
+
+struct toy_calculator
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_text_label disp;          /* The display. */
+  char digits[16];             /* Display digits (only 10+1 used). */
+  double reg;                  /* Hidden register. */
+  int op;                      /* Operation: PLUS, MINUS, TIMES, DIVIDE. */
+  ml_box box;                  /* The top-level box. */
+};
+
+/* Lots of callback functions for each button. */
+static void press_button_0 (ml_session, void *);
+static void press_button_1 (ml_session, void *);
+static void press_button_2 (ml_session, void *);
+static void press_button_3 (ml_session, void *);
+static void press_button_4 (ml_session, void *);
+
+static void press_button_5 (ml_session, void *);
+static void press_button_6 (ml_session, void *);
+static void press_button_7 (ml_session, void *);
+static void press_button_8 (ml_session, void *);
+static void press_button_9 (ml_session, void *);
+
+static void press_button_DOT (ml_session, void *);
+static void press_button_EQUALS (ml_session, void *);
+static void press_button_PLUS (ml_session, void *);
+static void press_button_MINUS (ml_session, void *);
+static void press_button_TIMES (ml_session, void *);
+static void press_button_DIVIDE (ml_session, void *);
+static void press_button_CLEAR (ml_session, void *);
+static void press_button_AC (ml_session, void *);
+
+/* These are used as indexes in the b[] array. */
+#define DOT 10
+#define EQUALS 11
+#define PLUS 12
+#define MINUS 13
+#define TIMES 14
+#define DIVIDE 15
+#define CLEAR 16
+#define AC 17
+
+toy_calculator
+new_toy_calculator (pool pool, ml_session session)
+{
+  toy_calculator w;
+  ml_box box;
+  ml_table_layout tbl;
+  ml_button b[18];
+  ml_text_label disp;
+
+  w = pmalloc (pool, sizeof *w);
+  w->ops = &toy_calculator_ops;
+  w->pool = pool;
+  strcpy (w->digits, "0");
+  w->reg = 0;
+  w->op = 0;
+
+  /* Create the box surrounding the calculator. */
+  box = new_ml_box (pool);
+
+  /* A table layout widget is used to arrange the buttons and the screen.
+   * There are 6 rows, each with 4 columns.
+   */
+  tbl = new_ml_table_layout (pool, 6, 4);
+
+  /* Create the numeric buttons. */
+  b[0] = new_ml_button (pool, "0");
+  ml_button_set_callback (b[0], press_button_0, session, w);
+  ml_widget_set_property (b[0], "button.style", "key");
+  b[1] = new_ml_button (pool, "1");
+  ml_button_set_callback (b[1], press_button_1, session, w);
+  ml_widget_set_property (b[1], "button.style", "key");
+  b[2] = new_ml_button (pool, "2");
+  ml_button_set_callback (b[2], press_button_2, session, w);
+  ml_widget_set_property (b[2], "button.style", "key");
+  b[3] = new_ml_button (pool, "3");
+  ml_button_set_callback (b[3], press_button_3, session, w);
+  ml_widget_set_property (b[3], "button.style", "key");
+  b[4] = new_ml_button (pool, "4");
+  ml_button_set_callback (b[4], press_button_4, session, w);
+  ml_widget_set_property (b[4], "button.style", "key");
+
+  b[5] = new_ml_button (pool, "5");
+  ml_button_set_callback (b[5], press_button_5, session, w);
+  ml_widget_set_property (b[5], "button.style", "key");
+  b[6] = new_ml_button (pool, "6");
+  ml_button_set_callback (b[6], press_button_6, session, w);
+  ml_widget_set_property (b[6], "button.style", "key");
+  b[7] = new_ml_button (pool, "7");
+  ml_button_set_callback (b[7], press_button_7, session, w);
+  ml_widget_set_property (b[7], "button.style", "key");
+  b[8] = new_ml_button (pool, "8");
+  ml_button_set_callback (b[8], press_button_8, session, w);
+  ml_widget_set_property (b[8], "button.style", "key");
+  b[9] = new_ml_button (pool, "9");
+  ml_button_set_callback (b[9], press_button_9, session, w);
+  ml_widget_set_property (b[9], "button.style", "key");
+
+  /* Create the other buttons. */
+  b[DOT] = new_ml_button (pool, ".");
+  ml_button_set_callback (b[DOT], press_button_DOT, session, w);
+  ml_widget_set_property (b[DOT], "button.style", "key");
+  b[EQUALS] = new_ml_button (pool, "=");
+  ml_button_set_callback (b[EQUALS], press_button_EQUALS, session, w);
+  ml_widget_set_property (b[EQUALS], "button.style", "key");
+  b[PLUS] = new_ml_button (pool, "+");
+  ml_button_set_callback (b[PLUS], press_button_PLUS, session, w);
+  ml_widget_set_property (b[PLUS], "button.style", "key");
+  b[MINUS] = new_ml_button (pool, "-");
+  ml_button_set_callback (b[MINUS], press_button_MINUS, session, w);
+  ml_widget_set_property (b[MINUS], "button.style", "key");
+  b[TIMES] = new_ml_button (pool, "x");
+  ml_button_set_callback (b[TIMES], press_button_TIMES, session, w);
+  ml_widget_set_property (b[TIMES], "button.style", "key");
+  b[DIVIDE] = new_ml_button (pool, "/");
+  ml_button_set_callback (b[DIVIDE], press_button_DIVIDE, session, w);
+  ml_widget_set_property (b[DIVIDE], "button.style", "key");
+  b[CLEAR] = new_ml_button (pool, "C");
+  ml_button_set_callback (b[CLEAR], press_button_CLEAR, session, w);
+  ml_widget_set_property (b[CLEAR], "button.style", "key");
+  b[AC] = new_ml_button (pool, "AC");
+  ml_button_set_callback (b[AC], press_button_AC, session, w);
+
+  /* Create the display. */
+  disp = new_ml_text_label (pool, "0");
+  ml_widget_set_property (disp, "font.weight", "bold");
+  ml_widget_set_property (disp, "font.size", "large");
+
+  /* Pack the buttons and display into the table layout widget. */
+  ml_table_layout_pack (tbl, disp, 0, 0);
+  ml_table_layout_set_colspan (tbl, 0, 0, 4);
+  ml_table_layout_set_align (tbl, 0, 0, "right");
+
+  ml_table_layout_pack (tbl, b[CLEAR], 1, 2);
+  ml_table_layout_pack (tbl, b[AC], 1, 3);
+
+  ml_table_layout_pack (tbl, b[7], 2, 0);
+  ml_table_layout_pack (tbl, b[8], 2, 1);
+  ml_table_layout_pack (tbl, b[9], 2, 2);
+  ml_table_layout_pack (tbl, b[DIVIDE], 2, 3);
+
+  ml_table_layout_pack (tbl, b[4], 3, 0);
+  ml_table_layout_pack (tbl, b[5], 3, 1);
+  ml_table_layout_pack (tbl, b[6], 3, 2);
+  ml_table_layout_pack (tbl, b[TIMES], 3, 3);
+
+  ml_table_layout_pack (tbl, b[1], 4, 0);
+  ml_table_layout_pack (tbl, b[2], 4, 1);
+  ml_table_layout_pack (tbl, b[3], 4, 2);
+  ml_table_layout_pack (tbl, b[MINUS], 4, 3);
+
+  ml_table_layout_pack (tbl, b[DOT], 5, 0);
+  ml_table_layout_pack (tbl, b[0], 5, 1);
+  ml_table_layout_pack (tbl, b[EQUALS], 5, 2);
+  ml_table_layout_pack (tbl, b[PLUS], 5, 3);
+
+  /* Pack the table into the box. */
+  ml_box_pack (box, tbl);
+
+  /* Save the display widget in the widget structure so that the
+   * callback functions can update it.
+   */
+  w->disp = disp;
+
+  /* Save the box, so we can repaint. */
+  w->box = box;
+
+  return w;
+}
+
+static void press_button_N (toy_calculator, int);
+
+static void
+press_button_0 (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  press_button_N (w, 0);
+}
+
+static void
+press_button_1 (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  press_button_N (w, 1);
+}
+
+static void
+press_button_2 (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  press_button_N (w, 2);
+}
+
+static void
+press_button_3 (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  press_button_N (w, 3);
+}
+
+static void
+press_button_4 (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  press_button_N (w, 4);
+}
+
+static void
+press_button_5 (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  press_button_N (w, 5);
+}
+
+static void
+press_button_6 (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  press_button_N (w, 6);
+}
+
+static void
+press_button_7 (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  press_button_N (w, 7);
+}
+
+static void
+press_button_8 (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  press_button_N (w, 8);
+}
+
+static void
+press_button_9 (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  press_button_N (w, 9);
+}
+
+static void
+press_button_N (toy_calculator w, int n)
+{
+  int len;
+
+  if (strcmp (w->digits, "0") == 0)
+    w->digits[0] = '\0';
+
+  len = strlen (w->digits);
+
+  if ((strchr (w->digits, '.') && len < 11) || len < 10)
+    {
+      w->digits[len] = '0' + n;
+      w->digits[len+1] = '\0';
+      ml_widget_set_property (w->disp, "text", w->digits);
+    }
+}
+
+static void
+press_button_DOT (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+  int len = strlen (w->digits);
+
+  if (strchr (w->digits, '.') == 0 && len < 10)
+    {
+      strcat (w->digits, ".");
+      ml_widget_set_property (w->disp, "text", w->digits);
+    }
+}
+
+static void
+press_button_EQUALS (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+  double a;
+
+  if (w->op)
+    {
+      sscanf (w->digits, "%lf", &a);
+
+      if (w->op == PLUS)
+       w->reg += a;
+      else if (w->op == MINUS)
+       w->reg -= a;
+      else if (w->op == TIMES)
+       w->reg *= a;
+      else if (w->op == DIVIDE && a != 0)
+       w->reg /= a;
+
+      snprintf (w->digits, 10, "%g", w->reg);
+      ml_widget_set_property (w->disp, "text", w->digits);
+      w->op = 0;
+    }
+}
+
+static void
+press_button_PLUS (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  /* Act like we just pressed the EQUALS key. */
+  press_button_EQUALS (session, vw);
+
+  sscanf (w->digits, "%lf", &w->reg);
+  strcpy (w->digits, "0");     /* But DON'T update the label yet. */
+  w->op = PLUS;
+}
+
+static void
+press_button_MINUS (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  /* Act like we just pressed the EQUALS key. */
+  press_button_EQUALS (session, vw);
+
+  sscanf (w->digits, "%lf", &w->reg);
+  strcpy (w->digits, "0");     /* But DON'T update the label yet. */
+  w->op = MINUS;
+}
+
+static void
+press_button_TIMES (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  /* Act like we just pressed the EQUALS key. */
+  press_button_EQUALS (session, vw);
+
+  sscanf (w->digits, "%lf", &w->reg);
+  strcpy (w->digits, "0");     /* But DON'T update the label yet. */
+  w->op = TIMES;
+}
+
+static void
+press_button_DIVIDE (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  /* Act like we just pressed the EQUALS key. */
+  press_button_EQUALS (session, vw);
+
+  sscanf (w->digits, "%lf", &w->reg);
+  strcpy (w->digits, "0");     /* But DON'T update the label yet. */
+  w->op = DIVIDE;
+}
+
+static void
+press_button_CLEAR (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  strcpy (w->digits, "0");
+  ml_widget_set_property (w->disp, "text", "0");
+}
+
+static void
+press_button_AC (ml_session session, void *vw)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  strcpy (w->digits, "0");
+  w->reg = 0;
+  ml_widget_set_property (w->disp, "text", "0");
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  toy_calculator w = (toy_calculator) vw;
+
+  ml_widget_repaint (w->box, session, windowid, io);
+}
diff --git a/examples/toy_calculator.h b/examples/toy_calculator.h
new file mode 100644 (file)
index 0000000..9d6ad83
--- /dev/null
@@ -0,0 +1,37 @@
+/* Toy calculator from example 02 turned into a reusable widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: toy_calculator.h,v 1.1 2002/09/07 13:46:58 rich Exp $
+ */
+
+#ifndef TOY_CALCULATOR_H
+#define TOY_CALCULATOR_H
+
+#include <pool.h>
+#include "monolith.h"
+
+struct toy_calculator;
+typedef struct toy_calculator *toy_calculator;
+
+/* Function: new_toy_calculator - toy calculator widget
+ *
+ * @code{new_toy_calculator} creates a new reusable toy calculator
+ * widget.
+ */
+extern toy_calculator new_toy_calculator (pool, ml_session);
+
+#endif /* TOY_CALCULATOR_H */
diff --git a/icons/addrbook.png b/icons/addrbook.png
new file mode 100644 (file)
index 0000000..136da6e
Binary files /dev/null and b/icons/addrbook.png differ
diff --git a/icons/addrbook.ppm b/icons/addrbook.ppm
new file mode 100644 (file)
index 0000000..1a86691
Binary files /dev/null and b/icons/addrbook.ppm differ
diff --git a/icons/ledge.png b/icons/ledge.png
new file mode 100644 (file)
index 0000000..a47b008
Binary files /dev/null and b/icons/ledge.png differ
diff --git a/icons/ledge.ppm b/icons/ledge.ppm
new file mode 100644 (file)
index 0000000..769c9de
--- /dev/null
@@ -0,0 +1,5 @@
+P6
+# CREATOR: The GIMP's PNM Filter Version 1.0
+16 25
+255
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²²²²²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²²²²²²²²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²²²²²²²²²²²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
\ No newline at end of file
diff --git a/icons/ledgeu.png b/icons/ledgeu.png
new file mode 100644 (file)
index 0000000..2988e93
Binary files /dev/null and b/icons/ledgeu.png differ
diff --git a/icons/ledgeu.ppm b/icons/ledgeu.ppm
new file mode 100644 (file)
index 0000000..1b10520
--- /dev/null
@@ -0,0 +1,5 @@
+P6
+# CREATOR: The GIMP's PNM Filter Version 1.0
+16 25
+255
+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²²²²²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²²²²²²²²²²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
\ No newline at end of file
diff --git a/icons/loverr.png b/icons/loverr.png
new file mode 100644 (file)
index 0000000..4729cbc
Binary files /dev/null and b/icons/loverr.png differ
diff --git a/icons/loverr.ppm b/icons/loverr.ppm
new file mode 100644 (file)
index 0000000..9553693
Binary files /dev/null and b/icons/loverr.ppm differ
diff --git a/icons/loverru.png b/icons/loverru.png
new file mode 100644 (file)
index 0000000..cca140f
Binary files /dev/null and b/icons/loverru.png differ
diff --git a/icons/loverru.ppm b/icons/loverru.ppm
new file mode 100644 (file)
index 0000000..5dc3bb6
Binary files /dev/null and b/icons/loverru.ppm differ
diff --git a/icons/redge.png b/icons/redge.png
new file mode 100644 (file)
index 0000000..b79aa79
Binary files /dev/null and b/icons/redge.png differ
diff --git a/icons/redge.ppm b/icons/redge.ppm
new file mode 100644 (file)
index 0000000..a661047
Binary files /dev/null and b/icons/redge.ppm differ
diff --git a/icons/redgeu.png b/icons/redgeu.png
new file mode 100644 (file)
index 0000000..672dfb4
Binary files /dev/null and b/icons/redgeu.png differ
diff --git a/icons/redgeu.ppm b/icons/redgeu.ppm
new file mode 100644 (file)
index 0000000..fa67267
Binary files /dev/null and b/icons/redgeu.ppm differ
diff --git a/icons/roverl.png b/icons/roverl.png
new file mode 100644 (file)
index 0000000..5023772
Binary files /dev/null and b/icons/roverl.png differ
diff --git a/icons/roverl.ppm b/icons/roverl.ppm
new file mode 100644 (file)
index 0000000..656f0cb
Binary files /dev/null and b/icons/roverl.ppm differ
diff --git a/icons/roverlu.png b/icons/roverlu.png
new file mode 100644 (file)
index 0000000..3170cf1
Binary files /dev/null and b/icons/roverlu.png differ
diff --git a/icons/roverlu.ppm b/icons/roverlu.ppm
new file mode 100644 (file)
index 0000000..502c31b
Binary files /dev/null and b/icons/roverlu.ppm differ
diff --git a/sql/ml_bulletins_create.sql b/sql/ml_bulletins_create.sql
new file mode 100644 (file)
index 0000000..e5a8b19
--- /dev/null
@@ -0,0 +1,83 @@
+-- Create schema for ml_bulletins widget.
+-- - by Richard W.M. Jones <rich@annexia.org>
+--
+-- This library is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU Library General Public
+-- License as published by the Free Software Foundation; either
+-- version 2 of the License, or (at your option) any later version.
+--
+-- This library is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+-- Library General Public License for more details.
+--
+-- You should have received a copy of the GNU Library General Public
+-- License along with this library; if not, write to the Free
+-- Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+--
+-- $Id: ml_bulletins_create.sql,v 1.7 2002/11/15 20:46:03 rich Exp $
+--
+-- Depends: monolith_core, monolith_users, monolith_auth, monolith_resources
+
+begin work;
+
+create table ml_bulletins_sections
+(
+       resid int4
+               constraint ml_bulletins_sections_resid_pk
+               primary key
+               references ml_resources (resid)
+               on delete cascade
+);
+
+create table ml_bulletins_posters
+(
+       sectionid int4          -- The section
+               references ml_bulletins_sections (resid)
+               on delete cascade,
+       userid int4             -- The user who can post in this section
+               references ml_users (userid)
+               on delete cascade
+);
+
+create unique index ml_bulletins_posters_ui
+       on ml_bulletins_posters (sectionid, userid);
+
+create table ml_bulletins
+(
+       id serial,
+       sectionid int4          -- Which section is this in?
+               constraint ml_bulletins_sectionid_nn
+               not null
+               references ml_bulletins_sections (resid)
+               on delete cascade,
+       authorid int4
+               constraint ml_bulletins_authorid_nn
+               not null
+               references ml_users (userid)
+               on delete cascade,
+       item text               -- The text body of the item
+               constraint ml_bulletins_item_nn
+               not null,
+       item_type char(1)       -- Type: plain, smart, HTML
+               constraint ml_bulletins_item_type_nn
+               not null
+               constraint ml_bulletins_item_type_ck
+               check (item_type in ('p', 's', 'h')),
+       posted_date timestamp   -- Date that this item was posted
+               default current_timestamp
+               constraint ml_bulletins_timestamp_nn
+               not null,
+       link text,              -- Optional link
+       link_text text          -- Optional text on the link
+);
+
+create index ml_bulletins_sectionid_i on ml_bulletins (sectionid);
+
+-- Allow the web server to access this table.
+grant select, insert, update, delete on ml_bulletins_sections to nobody;
+grant select, insert, delete on ml_bulletins_posters to nobody;
+grant select, insert, update, delete on ml_bulletins to nobody;
+grant select, update on ml_bulletins_id_seq to nobody;
+
+commit work;
\ No newline at end of file
diff --git a/sql/ml_bulletins_drop.sql b/sql/ml_bulletins_drop.sql
new file mode 100644 (file)
index 0000000..3e7afec
--- /dev/null
@@ -0,0 +1,27 @@
+-- Drop schema for ml_bulletins widget.
+-- - by Richard W.M. Jones <rich@annexia.org>
+--
+-- This library is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU Library General Public
+-- License as published by the Free Software Foundation; either
+-- version 2 of the License, or (at your option) any later version.
+--
+-- This library is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+-- Library General Public License for more details.
+--
+-- You should have received a copy of the GNU Library General Public
+-- License along with this library; if not, write to the Free
+-- Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+--
+-- $Id: ml_bulletins_drop.sql,v 1.4 2002/10/22 12:31:02 rich Exp $
+
+drop table ml_bulletins_sections;
+
+drop index ml_bulletins_posters_ui;
+drop table ml_bulletins_posters;
+
+drop index ml_bulletins_sectionid_i;
+drop table ml_bulletins;
+drop sequence ml_bulletins_id_seq;
\ No newline at end of file
diff --git a/sql/ml_calendar_create.sql b/sql/ml_calendar_create.sql
new file mode 100644 (file)
index 0000000..1404586
--- /dev/null
@@ -0,0 +1,82 @@
+-- Create schema for monolith calendar widget.
+-- Copyright (C) 2002 Richard W.M. Jones <rich@annexia.org>
+-- This code is NOT REDISTRIBUTABLE. To use this widget you must purchase
+-- a license at http://www.annexia.org/
+--
+-- $Id: ml_calendar_create.sql,v 1.1 2003/02/22 12:49:20 rich Exp $
+--
+-- Depends: monolith_core, monolith_users, monolith_auth, monolith_resources
+
+begin work;
+
+create table ml_calendar
+(
+       resid int4
+               constraint ml_calendar_resid_pk
+               primary key
+               references ml_resources (resid)
+               on delete cascade
+);
+
+create table ml_calendar_events
+(
+       id serial,              -- Unique event number
+       resid int4              -- Which calendar does this belong to?
+               constraint ml_calendar_events_resid_nn
+               not null
+               references ml_calendar (resid)
+               on delete cascade,
+
+       -- Start and length of the event. As a special case, events
+       -- which have length == 24 hours are day events, and are shown
+       -- as notes next to each day instead of on the calendar.
+       start_time timestamp    -- Start of the event
+               constraint ml_calendar_events_start_time_nn
+               not null,
+       length interval         -- Length of the event
+               constraint ml_calendar_events_length_nn
+               not null,
+
+       -- One-off events set these to null. Recurring events set period
+       -- to period of recurrence, usually 1 day, 1 week, 1 month or 1 year.
+       -- If the last_time is set, then a recurring event ceases _after_
+       -- this time.
+       period interval,        -- Recurrence period
+       last_time timestamp,    -- Last recurrence of the event
+
+       alert interval,         -- Send alert this interval before event
+       last_alert timestamp,   -- Last date an alert was sent for this event
+       alert_userid int4       -- Who to send the alert to
+               references ml_users (userid)
+               on delete set null,
+
+       subject text            -- Short text description
+               constraint ml_calendar_events_title_nn
+               not null,
+       body text,              -- Optional longer note
+
+       posted_date timestamp   -- When entered into calendar
+               default current_timestamp
+               constraint ml_calendar_events_posted_date_nn
+               not null,
+       last_modified_date timestamp -- When last modified
+               default current_timestamp
+               constraint ml_calendar_last_modified_date_nn
+               not null,
+       author int4             -- Who last modified
+               constraint ml_calendar_events_author_nn
+               not null
+               references ml_users (userid)
+               on delete cascade,
+       original_ip inet        -- Originating IP address when posted
+               constraint ml_calendar_events_original_ip_nn
+               not null
+);
+
+-- Grant access to the webservers
+
+grant select on ml_calendar to nobody;
+grant select, insert, update, delete on ml_calendar_events to nobody;
+grant select, update on ml_calendar_events_id_seq to nobody;
+
+commit work;
\ No newline at end of file
diff --git a/sql/ml_calendar_drop.sql b/sql/ml_calendar_drop.sql
new file mode 100644 (file)
index 0000000..5a4729d
--- /dev/null
@@ -0,0 +1,11 @@
+-- Drop schema for monolith calendar widget.
+-- Copyright (C) 2002 Richard W.M. Jones <rich@annexia.org>
+-- This code is NOT REDISTRIBUTABLE. To use this widget you must purchase
+-- a license at http://www.annexia.org/
+--
+-- $Id: ml_calendar_drop.sql,v 1.1 2003/02/22 12:49:21 rich Exp $
+
+drop table ml_calendar_events;
+drop sequence ml_calendar_events_id_seq;
+
+drop table ml_calendar;
\ No newline at end of file
diff --git a/sql/ml_chat_create.sql b/sql/ml_chat_create.sql
new file mode 100644 (file)
index 0000000..36d8639
--- /dev/null
@@ -0,0 +1,94 @@
+-- Create schema for monolith chat widget.
+-- Copyright (C) 2002 Richard W.M. Jones <rich@annexia.org>
+-- This code is NOT REDISTRIBUTABLE. To use this widget you must purchase
+-- a license at http://www.annexia.org/
+--
+-- $Id: ml_chat_create.sql,v 1.1 2003/02/22 12:49:21 rich Exp $
+--
+-- Depends: monolith_core, monolith_users, monolith_auth, monolith_resources
+
+begin work;
+
+create table ml_chat_rooms
+(
+       resid int4
+               constraint ml_chat_rooms_resid_pk
+               primary key
+               references ml_resources (resid)
+               on delete cascade,
+       allow_anon boolean      -- If true, allow anonymous postings
+               default 'f'
+               constraint ml_chat_rooms_allow_anon_nn
+               not null
+);
+
+-- Postings are logged in this table. The chat software doesn't
+-- actually pull postings out of this table when displaying the
+-- page, because that would be too slow. This table is consulted
+-- when the server starts up to populate the chat server's internal
+-- data structures.
+
+create table ml_chat_log
+(
+       id serial,              -- Unique posting ID
+       resid int4              -- Which room does this belong to?
+               constraint ml_chat_log_resid_nn
+               not null
+               references ml_chat_rooms (resid)
+               on delete cascade,
+       body text               -- The posting
+               constraint ml_chat_log_body_nn
+               not null,
+       posted_date timestamp   -- When posted
+               default current_timestamp
+               constraint ml_chat_log_posted_date_nn
+               not null,
+       author int4             -- Who posted (null == anonymous)
+               references ml_users (userid)
+               on delete set null,
+       original_ip inet        -- Originating IP address when posted
+               constraint ml_chat_log_original_ip_nn
+               not null
+);
+
+create table ml_chat_fill_buffers
+(
+       size int2               -- Number of bytes to send
+               constraint ml_chat_fill_buffers_size_nn
+               not null
+               constraint ml_chat_fill_buffers_pk
+               primary key
+);
+
+create table ml_chat_userprefs
+(
+       userid int4             -- The user ID.
+               constraint ml_chat_userprefs_userid_nn
+               not null
+               references ml_users (userid)
+               on delete cascade,
+       fill_buffer int2        -- Size of the fill buffer.
+               default 4096
+               constraint ml_chat_userprefs_fill_buffer_nn
+               not null
+               references ml_chat_fill_buffers (size)
+);
+
+-- Populate the tables.
+
+insert into ml_chat_fill_buffers (size) values (0);
+insert into ml_chat_fill_buffers (size) values (1024);
+insert into ml_chat_fill_buffers (size) values (2048);
+insert into ml_chat_fill_buffers (size) values (4096);
+insert into ml_chat_fill_buffers (size) values (8192);
+insert into ml_chat_fill_buffers (size) values (16384);
+
+-- Grant access to the webservers
+
+grant select on ml_chat_rooms to nobody;
+grant select, insert on ml_chat_log to nobody;
+grant select, update on ml_chat_log_id_seq to nobody;
+grant select on ml_chat_fill_buffers to nobody;
+grant select, insert, update, delete on ml_chat_userprefs to nobody;
+
+commit work;
\ No newline at end of file
diff --git a/sql/ml_chat_drop.sql b/sql/ml_chat_drop.sql
new file mode 100644 (file)
index 0000000..7045e29
--- /dev/null
@@ -0,0 +1,15 @@
+-- Drop schema for monolith chat widget.
+-- Copyright (C) 2002 Richard W.M. Jones <rich@annexia.org>
+-- This code is NOT REDISTRIBUTABLE. To use this widget you must purchase
+-- a license at http://www.annexia.org/
+--
+-- $Id: ml_chat_drop.sql,v 1.1 2003/02/22 12:49:21 rich Exp $
+
+drop table ml_chat_userprefs;
+
+drop table ml_chat_fill_buffers;
+
+drop table ml_chat_log;
+drop sequence ml_chat_log_id_seq;
+
+drop table ml_chat_rooms;
diff --git a/sql/ml_discussion_create.sql b/sql/ml_discussion_create.sql
new file mode 100644 (file)
index 0000000..e191935
--- /dev/null
@@ -0,0 +1,125 @@
+-- Create schema for monolith discussion widget.
+-- Copyright (C) 2002 Richard W.M. Jones <rich@annexia.org>
+-- This code is NOT REDISTRIBUTABLE. To use this widget you must purchase
+-- a license at http://www.annexia.org/
+--
+-- $Id: ml_discussion_create.sql,v 1.1 2003/02/22 12:49:21 rich Exp $
+--
+-- Depends: monolith_core, monolith_users, monolith_auth, monolith_resources
+
+begin work;
+
+create table ml_discussion_group
+(
+       resid int4
+               constraint ml_discussion_group_resid_pk
+               primary key
+               references ml_resources (resid)
+               on delete cascade,
+       expiry_days int2,       -- Number of days to expire (null = never)
+       allow_anon boolean      -- If true, allow anonymous postings
+               default 'f'
+               constraint ml_discussion_group_allow_anon_nn
+               not null,
+       default_view char(1)    -- Default view (unless user overrides)
+               default '1'
+               constraint ml_discussion_group_default_view_nn
+               not null
+               constraint ml_discussion_group_default_view_ck
+               check (default_view in ('1', '2'))
+);
+
+create table ml_discussion_article
+(
+       id serial,              -- Unique article number.
+       resid int4              -- Which group is this in?
+               constraint ml_discussion_article_resid_nn
+               not null
+               references ml_resources (resid)
+               on delete cascade,
+       parent int4             -- Parent of this article (for threading)
+               references ml_discussion_article (id),
+       subject text            -- Subject line of the message
+               constraint ml_discussion_article_subject_nn
+               not null,
+       author int4             -- Author of the article (null = anonymous)
+               references ml_users (userid)
+               on delete set null,
+       body text               -- The actual body of the article
+               constraint ml_discussion_article_content_nn
+               not null,
+       body_type char(1)       -- Type: plain, smart, HTML
+               constraint ml_discussion_article_body_type_nn
+               not null
+               constraint ml_discussion_article_body_type_ck
+               check (body_type in ('p', 's', 'h')),
+       posted_date timestamp   -- When posted
+               default current_timestamp
+               constraint ml_discussion_article_posted_date_nn
+               not null,
+       original_ip inet        -- Originating IP address when posted
+               constraint ml_discussion_article_original_ip_nn
+               not null
+);
+
+-- This table stores which articles each user has read. To keep the size
+-- compact, we store ranges of article IDs, rather than having one row
+-- per (user, article) which could potentially make the table huge.
+
+create table ml_discussion_read
+(
+       resid int4              -- The group.
+               constraint ml_discussion_read_resid_nn
+               not null
+               references ml_discussion_group (resid)
+               on delete cascade,
+       userid int4             -- The user ID.
+               constraint ml_discussion_read_userid_nn
+               not null
+               references ml_users (userid)
+               on delete cascade,
+       low int4                -- User has read articles [ low, high-1 ]
+               constraint ml_discussion_read_low_nn
+               not null,
+       high int4
+               constraint ml_discussion_read_high_nn
+               not null
+);
+
+-- User preferences
+
+create table ml_discussion_userprefs
+(
+       userid int4             -- The user ID.
+               constraint ml_discussion_userprefs_userid_nn
+               not null
+               references ml_users (userid)
+               on delete cascade,
+       view char(1)            -- 1-pane or 2-pane view
+               default '1'
+               constraint ml_discussion_userprefs_view_nn
+               not null
+               constraint ml_discussion_userprefs_view_ck
+               check (view in ('1', '2')),
+       sort_order char(1)      -- Sort by date, username, subject
+               default 'd'
+               constraint ml_discussion_userprefs_sort_order_nn
+               not null
+               constraint ml_discussion_userprefs_sort_order_ck
+               check (view in ('d', 'D', 'n', 'N', 's', 'S'))
+);
+
+-- Function to count the number of unread articles in the given
+-- newsgroup.
+
+\i ml_discussion_functions.sql
+
+-- Grant access to the webservers
+
+grant select on ml_discussion_group to nobody;
+grant select, insert, update, delete on ml_discussion_article to nobody;
+grant select, update on ml_discussion_article_id_seq to nobody;
+grant select, insert, update, delete on ml_discussion_read to nobody;
+grant select, insert, update, delete on ml_discussion_userprefs to nobody;
+
+commit work;
\ No newline at end of file
diff --git a/sql/ml_discussion_drop.sql b/sql/ml_discussion_drop.sql
new file mode 100644 (file)
index 0000000..0e09919
--- /dev/null
@@ -0,0 +1,17 @@
+-- Drop schema for monolith discussion widget.
+-- Copyright (C) 2002 Richard W.M. Jones <rich@annexia.org>
+-- This code is NOT REDISTRIBUTABLE. To use this widget you must purchase
+-- a license at http://www.annexia.org/
+--
+-- $Id: ml_discussion_drop.sql,v 1.1 2003/02/22 12:49:21 rich Exp $
+
+drop function ml_discussion_unread;
+
+drop table ml_discussion_userprefs;
+
+drop table ml_discussion_read;
+
+drop table ml_discussion_article;
+drop sequence ml_discussion_article_id_seq;
+
+drop table ml_discussion_group;
diff --git a/sql/ml_discussion_functions.sql b/sql/ml_discussion_functions.sql
new file mode 100644 (file)
index 0000000..9426c39
--- /dev/null
@@ -0,0 +1,43 @@
+-- Create functions for monolith discussion widget.
+-- Copyright (C) 2002 Richard W.M. Jones <rich@annexia.org>
+-- This code is NOT REDISTRIBUTABLE. To use this widget you must purchase
+-- a license at http://www.annexia.org/
+--
+-- $Id: ml_discussion_functions.sql,v 1.1 2003/02/22 12:49:21 rich Exp $
+
+-- Function to count the number of unread articles in the given
+-- newsgroup.
+
+create or replace function ml_discussion_unread (integer, integer)
+       returns integer as '
+
+declare
+  my_userid alias for $1;
+  my_resid alias for $2;
+  rec record;
+  stmt text;
+
+begin
+
+  stmt := ''select count (id) as result
+              from ml_discussion_article
+             where resid = '' || my_resid || ''
+                   and not (false'';
+
+  for rec in select low, high from ml_discussion_read
+              where resid = my_resid and userid = my_userid
+  loop
+    stmt := stmt || '' or (id between '' || rec.low ||
+                    '' and '' || (rec.high-1) || '')'';
+  end loop;
+
+  stmt := stmt || '')'';
+
+  for rec in execute stmt
+  loop
+    return rec.result;
+  end loop;
+
+end;
+
+' language plpgsql;
diff --git a/sql/ml_user_directory_create.sql b/sql/ml_user_directory_create.sql
new file mode 100644 (file)
index 0000000..cfb0dfc
--- /dev/null
@@ -0,0 +1,43 @@
+-- Create schema for monolith user directory.
+-- - by Richard W.M. Jones <rich@annexia.org>
+--
+-- This library is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU Library General Public
+-- License as published by the Free Software Foundation; either
+-- version 2 of the License, or (at your option) any later version.
+--
+-- This library is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+-- Library General Public License for more details.
+--
+-- You should have received a copy of the GNU Library General Public
+-- License along with this library; if not, write to the Free
+-- Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+--
+-- $Id: ml_user_directory_create.sql,v 1.2 2002/11/15 20:46:03 rich Exp $
+--
+-- Depends: monolith_core, monolith_users
+
+-- The user directory is strictly "opt-in". If a user is not listed in
+-- this table, then they do not want to appear in the directory. We
+-- need to refine this in the future so that the ML_USERS table contains
+-- more information about what details people want to be revealed to
+-- others.
+
+begin work;
+
+create table ml_userdir_prefs
+(
+       userid int4             -- User ID
+               constraint ml_userdir_prefs_userid_pk
+               primary key
+               references ml_users (userid)
+               on delete cascade
+);
+
+-- Grant access to the webserver.
+
+grant select, insert, update, delete on ml_userdir_prefs to nobody;
+
+commit work;
\ No newline at end of file
diff --git a/sql/monolith_auth_create.sql b/sql/monolith_auth_create.sql
new file mode 100644 (file)
index 0000000..9f31ce5
--- /dev/null
@@ -0,0 +1,45 @@
+-- Create schema for monolith authentication table.
+-- - by Richard W.M. Jones <rich@annexia.org>
+--
+-- This library is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU Library General Public
+-- License as published by the Free Software Foundation; either
+-- version 2 of the License, or (at your option) any later version.
+--
+-- This library is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+-- Library General Public License for more details.
+--
+-- You should have received a copy of the GNU Library General Public
+-- License along with this library; if not, write to the Free
+-- Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+--
+-- $Id: monolith_auth_create.sql,v 1.2 2002/11/15 20:46:03 rich Exp $
+--
+-- Depends: monolith_core, monolith_users
+
+-- This table is used by monolith itself when you use the authentication
+-- functions (ml_session_login, ml_session_logout, ml_session_userid).
+-- This table depends on a table (or view?) called ml_users. Normally
+-- you would use the ml_users table defined in monolith_users_create.sql,
+-- but if you prefer you can modify this file to point to your own users
+-- table.
+
+begin work;
+
+create table ml_user_cookie
+(
+       userid int4
+               references ml_users (userid),
+       cookie char(32)
+);
+
+create unique index ml_user_cookie_userid_ui on ml_user_cookie (userid);
+create unique index ml_user_cookie_cookie_ui on ml_user_cookie (cookie);
+
+-- Grant access to the webserver.
+
+grant select, insert, delete on ml_user_cookie to nobody;
+
+commit work;
diff --git a/sql/monolith_auth_drop.sql b/sql/monolith_auth_drop.sql
new file mode 100644 (file)
index 0000000..a24ae4e
--- /dev/null
@@ -0,0 +1,31 @@
+-- Drop schema for monolith authentication table.
+-- - by Richard W.M. Jones <rich@annexia.org>
+--
+-- This library is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU Library General Public
+-- License as published by the Free Software Foundation; either
+-- version 2 of the License, or (at your option) any later version.
+--
+-- This library is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+-- Library General Public License for more details.
+--
+-- You should have received a copy of the GNU Library General Public
+-- License along with this library; if not, write to the Free
+-- Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+--
+-- $Id: monolith_auth_drop.sql,v 1.1 2002/10/19 16:09:29 rich Exp $
+--
+-- Depends: monolith_core, monolith_users
+
+-- This table is used by monolith itself when you use the authentication
+-- functions (ml_session_login, ml_session_logout, ml_session_userid).
+-- This table depends on a table (or view?) called ml_users. Normally
+-- you would use the ml_users table defined in monolith_users_create.sql,
+-- but if you prefer you can modify this file to point to your own users
+-- table.
+
+drop index ml_user_cookie_userid_ui;
+drop index ml_user_cookie_cookie_ui;
+drop table ml_user_cookie;
diff --git a/sql/monolith_core_create.sql b/sql/monolith_core_create.sql
new file mode 100644 (file)
index 0000000..48ceb87
--- /dev/null
@@ -0,0 +1,671 @@
+-- Create the core monolith schema
+-- - by Richard W.M. Jones <rich@annexia.org>
+--
+-- This library is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU Library General Public
+-- License as published by the Free Software Foundation; either
+-- version 2 of the License, or (at your option) any later version.
+--
+-- This library is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+-- Library General Public License for more details.
+--
+-- You should have received a copy of the GNU Library General Public
+-- License along with this library; if not, write to the Free
+-- Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+--
+-- $Id: monolith_core_create.sql,v 1.2 2002/11/15 20:46:03 rich Exp $
+
+begin work;
+
+create table ml_countries
+(
+       code char(2)
+               constraint ml_countries_code_nn
+               not null,
+       name text
+               constraint ml_countries_name_nn
+               not null,
+
+       primary key (code)
+);
+
+create unique index ml_countries_name on ml_countries (name);
+
+create table ml_timezones
+(
+       id int4
+               constraint ml_timezones_id_nn
+               not null,
+       name text
+               constraint ml_timezones_name_nn
+               not null,
+       countrycode char(2)
+               constraint ml_timezones_countrycode_nn
+               not null
+               references ml_countries (code),
+
+       primary key (id)
+);
+
+create unique index ml_timezones_name on ml_timezones (name);
+
+-- Grant access to the tables by the webserver.
+
+grant select on ml_countries to nobody;
+grant select on ml_timezones to nobody;
+
+-- Populate the tables.
+
+insert into ml_countries (code,name) values ('AD','Andorra');
+insert into ml_countries (code,name) values ('AE','United Arab Emirates');
+insert into ml_countries (code,name) values ('AF','Afghanistan');
+insert into ml_countries (code,name) values ('AG','Antigua and Barbuda');
+insert into ml_countries (code,name) values ('AI','Anguilla');
+insert into ml_countries (code,name) values ('AL','Albania');
+insert into ml_countries (code,name) values ('AM','Armenia');
+insert into ml_countries (code,name) values ('AN','Netherlands Antilles');
+insert into ml_countries (code,name) values ('AO','Angola');
+insert into ml_countries (code,name) values ('AQ','Antarctica');
+insert into ml_countries (code,name) values ('AR','Argentina');
+insert into ml_countries (code,name) values ('AS','American Samoa');
+insert into ml_countries (code,name) values ('AT','Austria');
+insert into ml_countries (code,name) values ('AU','Australia');
+insert into ml_countries (code,name) values ('AW','Aruba');
+insert into ml_countries (code,name) values ('AZ','Azerbaijan');
+insert into ml_countries (code,name) values ('BA','Bosnia and Herzegovina');
+insert into ml_countries (code,name) values ('BB','Barbados');
+insert into ml_countries (code,name) values ('BD','Bangladesh');
+insert into ml_countries (code,name) values ('BE','Belgium');
+insert into ml_countries (code,name) values ('BF','Burkina Faso');
+insert into ml_countries (code,name) values ('BG','Bulgaria');
+insert into ml_countries (code,name) values ('BH','Bahrain');
+insert into ml_countries (code,name) values ('BI','Burundi');
+insert into ml_countries (code,name) values ('BJ','Benin');
+insert into ml_countries (code,name) values ('BM','Bermuda');
+insert into ml_countries (code,name) values ('BN','Brunei Darussalam');
+insert into ml_countries (code,name) values ('BO','Bolivia');
+insert into ml_countries (code,name) values ('BR','Brazil');
+insert into ml_countries (code,name) values ('BS','Bahamas');
+insert into ml_countries (code,name) values ('BT','Bhutan');
+insert into ml_countries (code,name) values ('BV','Bouvet Island');
+insert into ml_countries (code,name) values ('BW','Botswana');
+insert into ml_countries (code,name) values ('BY','Belarus');
+insert into ml_countries (code,name) values ('BZ','Belize');
+insert into ml_countries (code,name) values ('CA','Canada');
+insert into ml_countries (code,name) values ('CC','Cocos (Keeling) Islands');
+insert into ml_countries (code,name) values ('CD','Congo, The Democratic Republic of The');
+insert into ml_countries (code,name) values ('CF','Central African Republic');
+insert into ml_countries (code,name) values ('CG','Congo');
+insert into ml_countries (code,name) values ('CH','Switzerland');
+insert into ml_countries (code,name) values ('CI','Cote D''Ivoire');
+insert into ml_countries (code,name) values ('CK','Cook Islands');
+insert into ml_countries (code,name) values ('CL','Chile');
+insert into ml_countries (code,name) values ('CM','Cameroon');
+insert into ml_countries (code,name) values ('CN','China');
+insert into ml_countries (code,name) values ('CO','Colombia');
+insert into ml_countries (code,name) values ('CR','Costa Rica');
+insert into ml_countries (code,name) values ('CU','Cuba');
+insert into ml_countries (code,name) values ('CV','Cape Verde');
+insert into ml_countries (code,name) values ('CX','Christmas Island');
+insert into ml_countries (code,name) values ('CY','Cyprus');
+insert into ml_countries (code,name) values ('CZ','Czech Republic');
+insert into ml_countries (code,name) values ('DE','Germany');
+insert into ml_countries (code,name) values ('DJ','Djibouti');
+insert into ml_countries (code,name) values ('DK','Denmark');
+insert into ml_countries (code,name) values ('DM','Dominica');
+insert into ml_countries (code,name) values ('DO','Dominican Republic');
+insert into ml_countries (code,name) values ('DZ','Algeria');
+insert into ml_countries (code,name) values ('EC','Ecuador');
+insert into ml_countries (code,name) values ('EE','Estonia');
+insert into ml_countries (code,name) values ('EG','Egypt');
+insert into ml_countries (code,name) values ('EH','Western Sahara');
+insert into ml_countries (code,name) values ('ER','Eritrea');
+insert into ml_countries (code,name) values ('ES','Spain');
+insert into ml_countries (code,name) values ('ET','Ethiopia');
+insert into ml_countries (code,name) values ('FI','Finland');
+insert into ml_countries (code,name) values ('FJ','Fiji');
+insert into ml_countries (code,name) values ('FK','Falkland Islands (Malvinas)');
+insert into ml_countries (code,name) values ('FM','Micronesia, Federated States of');
+insert into ml_countries (code,name) values ('FO','Faroe Islands');
+insert into ml_countries (code,name) values ('FR','France');
+insert into ml_countries (code,name) values ('GA','Gabon');
+insert into ml_countries (code,name) values ('GB','United Kingdom');
+insert into ml_countries (code,name) values ('GD','Grenada');
+insert into ml_countries (code,name) values ('GE','Georgia');
+insert into ml_countries (code,name) values ('GF','French Guiana');
+insert into ml_countries (code,name) values ('GH','Ghana');
+insert into ml_countries (code,name) values ('GI','Gibraltar');
+insert into ml_countries (code,name) values ('GL','Greenland');
+insert into ml_countries (code,name) values ('GM','Gambia');
+insert into ml_countries (code,name) values ('GN','Guinea');
+insert into ml_countries (code,name) values ('GP','Guadeloupe');
+insert into ml_countries (code,name) values ('GQ','Equatorial Guinea');
+insert into ml_countries (code,name) values ('GR','Greece');
+insert into ml_countries (code,name) values ('GS','South Georgia and The South Sandwich Islands');
+insert into ml_countries (code,name) values ('GT','Guatemala');
+insert into ml_countries (code,name) values ('GU','Guam');
+insert into ml_countries (code,name) values ('GW','Guinea-Bissau');
+insert into ml_countries (code,name) values ('GY','Guyana');
+insert into ml_countries (code,name) values ('HK','Hong Kong');
+insert into ml_countries (code,name) values ('HM','Heard Island and McDonald Islands');
+insert into ml_countries (code,name) values ('HN','Honduras');
+insert into ml_countries (code,name) values ('HR','Croatia');
+insert into ml_countries (code,name) values ('HT','Haiti');
+insert into ml_countries (code,name) values ('HU','Hungary');
+insert into ml_countries (code,name) values ('ID','Indonesia');
+insert into ml_countries (code,name) values ('IE','Ireland');
+insert into ml_countries (code,name) values ('IL','Israel');
+insert into ml_countries (code,name) values ('IN','India');
+insert into ml_countries (code,name) values ('IO','British Indian Ocean Territory');
+insert into ml_countries (code,name) values ('IQ','Iraq');
+insert into ml_countries (code,name) values ('IR','Iran, Islamic Republic of');
+insert into ml_countries (code,name) values ('IS','Iceland');
+insert into ml_countries (code,name) values ('IT','Italy');
+insert into ml_countries (code,name) values ('JM','Jamaica');
+insert into ml_countries (code,name) values ('JO','Jordan');
+insert into ml_countries (code,name) values ('JP','Japan');
+insert into ml_countries (code,name) values ('KE','Kenya');
+insert into ml_countries (code,name) values ('KG','Kyrgyzstan');
+insert into ml_countries (code,name) values ('KH','Cambodia');
+insert into ml_countries (code,name) values ('KI','Kiribati');
+insert into ml_countries (code,name) values ('KM','Comoros');
+insert into ml_countries (code,name) values ('KN','Saint Kitts and Nevis');
+insert into ml_countries (code,name) values ('KP','Korea, Democratic People''s Republic of');
+insert into ml_countries (code,name) values ('KR','Korea, Republic of');
+insert into ml_countries (code,name) values ('KW','Kuwait');
+insert into ml_countries (code,name) values ('KY','Cayman Islands');
+insert into ml_countries (code,name) values ('KZ','Kazakstan');
+insert into ml_countries (code,name) values ('LA','Lao People''s Democratic Republic');
+insert into ml_countries (code,name) values ('LB','Lebanon');
+insert into ml_countries (code,name) values ('LC','Saint Lucia');
+insert into ml_countries (code,name) values ('LI','Liechtenstein');
+insert into ml_countries (code,name) values ('LK','Sri Lanka');
+insert into ml_countries (code,name) values ('LR','Liberia');
+insert into ml_countries (code,name) values ('LS','Lesotho');
+insert into ml_countries (code,name) values ('LT','Lithuania');
+insert into ml_countries (code,name) values ('LU','Luxembourg');
+insert into ml_countries (code,name) values ('LV','Latvia');
+insert into ml_countries (code,name) values ('LY','Libyan Arab Jamahiriya');
+insert into ml_countries (code,name) values ('MA','Morocco');
+insert into ml_countries (code,name) values ('MC','Monaco');
+insert into ml_countries (code,name) values ('MD','Moldova, Republic of');
+insert into ml_countries (code,name) values ('MG','Madagascar');
+insert into ml_countries (code,name) values ('MH','Marshall Islands');
+insert into ml_countries (code,name) values ('MK','Macedonia, The Former Yugoslav Republic of');
+insert into ml_countries (code,name) values ('ML','Mali');
+insert into ml_countries (code,name) values ('MM','Myanmar');
+insert into ml_countries (code,name) values ('MN','Mongolia');
+insert into ml_countries (code,name) values ('MO','Macau');
+insert into ml_countries (code,name) values ('MP','Northern Mariana Islands');
+insert into ml_countries (code,name) values ('MQ','Martinique');
+insert into ml_countries (code,name) values ('MR','Mauritania');
+insert into ml_countries (code,name) values ('MS','Montserrat');
+insert into ml_countries (code,name) values ('MT','Malta');
+insert into ml_countries (code,name) values ('MU','Mauritius');
+insert into ml_countries (code,name) values ('MV','Maldives');
+insert into ml_countries (code,name) values ('MW','Malawi');
+insert into ml_countries (code,name) values ('MX','Mexico');
+insert into ml_countries (code,name) values ('MY','Malaysia');
+insert into ml_countries (code,name) values ('MZ','Mozambique');
+insert into ml_countries (code,name) values ('NA','Namibia');
+insert into ml_countries (code,name) values ('NC','New Caledonia');
+insert into ml_countries (code,name) values ('NE','Niger');
+insert into ml_countries (code,name) values ('NF','Norfolk Island');
+insert into ml_countries (code,name) values ('NG','Nigeria');
+insert into ml_countries (code,name) values ('NI','Nicaragua');
+insert into ml_countries (code,name) values ('NL','Netherlands');
+insert into ml_countries (code,name) values ('NO','Norway');
+insert into ml_countries (code,name) values ('NP','Nepal');
+insert into ml_countries (code,name) values ('NR','Nauru');
+insert into ml_countries (code,name) values ('NU','Niue');
+insert into ml_countries (code,name) values ('NZ','New Zealand');
+insert into ml_countries (code,name) values ('OM','Oman');
+insert into ml_countries (code,name) values ('PA','Panama');
+insert into ml_countries (code,name) values ('PE','Peru');
+insert into ml_countries (code,name) values ('PF','French Polynesia');
+insert into ml_countries (code,name) values ('PG','Papua New Guinea');
+insert into ml_countries (code,name) values ('PH','Philippines');
+insert into ml_countries (code,name) values ('PK','Pakistan');
+insert into ml_countries (code,name) values ('PL','Poland');
+insert into ml_countries (code,name) values ('PM','Saint Pierre and Miquelon');
+insert into ml_countries (code,name) values ('PN','Pitcairn');
+insert into ml_countries (code,name) values ('PR','Puerto Rico');
+insert into ml_countries (code,name) values ('PS','Palestinian Territory, Occupied');
+insert into ml_countries (code,name) values ('PT','Portugal');
+insert into ml_countries (code,name) values ('PW','Palau');
+insert into ml_countries (code,name) values ('PY','Paraguay');
+insert into ml_countries (code,name) values ('QA','Qatar');
+insert into ml_countries (code,name) values ('RE','Reunion');
+insert into ml_countries (code,name) values ('RO','Romania');
+insert into ml_countries (code,name) values ('RU','Russian Federation');
+insert into ml_countries (code,name) values ('RW','Rwanda');
+insert into ml_countries (code,name) values ('SA','Saudi Arabia');
+insert into ml_countries (code,name) values ('SB','Solomon Islands');
+insert into ml_countries (code,name) values ('SC','Seychelles');
+insert into ml_countries (code,name) values ('SD','Sudan');
+insert into ml_countries (code,name) values ('SE','Sweden');
+insert into ml_countries (code,name) values ('SG','Singapore');
+insert into ml_countries (code,name) values ('SH','Saint Helena');
+insert into ml_countries (code,name) values ('SI','Slovenia');
+insert into ml_countries (code,name) values ('SJ','Svalbard and Jan Mayen');
+insert into ml_countries (code,name) values ('SK','Slovakia');
+insert into ml_countries (code,name) values ('SL','Sierra Leone');
+insert into ml_countries (code,name) values ('SM','San Marino');
+insert into ml_countries (code,name) values ('SN','Senegal');
+insert into ml_countries (code,name) values ('SO','Somalia');
+insert into ml_countries (code,name) values ('SR','Suriname');
+insert into ml_countries (code,name) values ('ST','Sao Tome and Principe');
+insert into ml_countries (code,name) values ('SV','El Salvador');
+insert into ml_countries (code,name) values ('SY','Syrian Arab Republic');
+insert into ml_countries (code,name) values ('SZ','Swaziland');
+insert into ml_countries (code,name) values ('TC','Turks and Caicos Islands');
+insert into ml_countries (code,name) values ('TD','Chad');
+insert into ml_countries (code,name) values ('TF','French Southern Territories');
+insert into ml_countries (code,name) values ('TG','Togo');
+insert into ml_countries (code,name) values ('TH','Thailand');
+insert into ml_countries (code,name) values ('TJ','Tajikistan');
+insert into ml_countries (code,name) values ('TK','Tokelau');
+insert into ml_countries (code,name) values ('TM','Turkmenistan');
+insert into ml_countries (code,name) values ('TN','Tunisia');
+insert into ml_countries (code,name) values ('TO','Tonga');
+insert into ml_countries (code,name) values ('TP','East Timor');
+insert into ml_countries (code,name) values ('TR','Turkey');
+insert into ml_countries (code,name) values ('TT','Trinidad and Tobago');
+insert into ml_countries (code,name) values ('TV','Tuvalu');
+insert into ml_countries (code,name) values ('TW','Taiwan, Province of China');
+insert into ml_countries (code,name) values ('TZ','Tanzania, United Republic of');
+insert into ml_countries (code,name) values ('UA','Ukraine');
+insert into ml_countries (code,name) values ('UG','Uganda');
+insert into ml_countries (code,name) values ('UM','United States Minor Outlying Islands');
+insert into ml_countries (code,name) values ('US','United States');
+insert into ml_countries (code,name) values ('UY','Uruguay');
+insert into ml_countries (code,name) values ('UZ','Uzbekistan');
+insert into ml_countries (code,name) values ('VA','Holy See (Vatican City State)');
+insert into ml_countries (code,name) values ('VC','Saint Vincent and The Grenadines');
+insert into ml_countries (code,name) values ('VE','Venezuela');
+insert into ml_countries (code,name) values ('VG','Virgin Islands, British');
+insert into ml_countries (code,name) values ('VI','Virgin Islands, U.S.');
+insert into ml_countries (code,name) values ('VN','Viet Nam');
+insert into ml_countries (code,name) values ('VU','Vanuatu');
+insert into ml_countries (code,name) values ('WF','Wallis and Futuna');
+insert into ml_countries (code,name) values ('WS','Samoa');
+insert into ml_countries (code,name) values ('YE','Yemen');
+insert into ml_countries (code,name) values ('YT','Mayotte');
+insert into ml_countries (code,name) values ('YU','Yugoslavia');
+insert into ml_countries (code,name) values ('ZA','South Africa');
+insert into ml_countries (code,name) values ('ZM','Zambia');
+insert into ml_countries (code,name) values ('ZW','Zimbabwe');
+
+insert into ml_timezones (countrycode,id,name) values ('AD','1','Europe/Andorra');
+insert into ml_timezones (countrycode,id,name) values ('AE','2','Asia/Dubai');
+insert into ml_timezones (countrycode,id,name) values ('AF','3','Asia/Kabul');
+insert into ml_timezones (countrycode,id,name) values ('AG','4','America/Antigua');
+insert into ml_timezones (countrycode,id,name) values ('AI','5','America/Anguilla');
+insert into ml_timezones (countrycode,id,name) values ('AL','6','Europe/Tirane');
+insert into ml_timezones (countrycode,id,name) values ('AM','7','Asia/Yerevan');
+insert into ml_timezones (countrycode,id,name) values ('AN','8','America/Curacao');
+insert into ml_timezones (countrycode,id,name) values ('AO','9','Africa/Luanda');
+insert into ml_timezones (countrycode,id,name) values ('AQ','10','Antarctica/McMurdo');
+insert into ml_timezones (countrycode,id,name) values ('AQ','11','Antarctica/South_Pole');
+insert into ml_timezones (countrycode,id,name) values ('AQ','12','Antarctica/Palmer');
+insert into ml_timezones (countrycode,id,name) values ('AQ','13','Antarctica/Mawson');
+insert into ml_timezones (countrycode,id,name) values ('AQ','14','Antarctica/Davis');
+insert into ml_timezones (countrycode,id,name) values ('AQ','15','Antarctica/Casey');
+insert into ml_timezones (countrycode,id,name) values ('AQ','16','Antarctica/Vostok');
+insert into ml_timezones (countrycode,id,name) values ('AQ','17','Antarctica/DumontDUrville');
+insert into ml_timezones (countrycode,id,name) values ('AQ','18','Antarctica/Syowa');
+insert into ml_timezones (countrycode,id,name) values ('AR','19','America/Buenos_Aires');
+insert into ml_timezones (countrycode,id,name) values ('AR','20','America/Rosario');
+insert into ml_timezones (countrycode,id,name) values ('AR','21','America/Cordoba');
+insert into ml_timezones (countrycode,id,name) values ('AR','22','America/Jujuy');
+insert into ml_timezones (countrycode,id,name) values ('AR','23','America/Catamarca');
+insert into ml_timezones (countrycode,id,name) values ('AR','24','America/Mendoza');
+insert into ml_timezones (countrycode,id,name) values ('AS','25','Pacific/Pago_Pago');
+insert into ml_timezones (countrycode,id,name) values ('AT','26','Europe/Vienna');
+insert into ml_timezones (countrycode,id,name) values ('AU','27','Australia/Lord_Howe');
+insert into ml_timezones (countrycode,id,name) values ('AU','28','Australia/Hobart');
+insert into ml_timezones (countrycode,id,name) values ('AU','29','Australia/Melbourne');
+insert into ml_timezones (countrycode,id,name) values ('AU','30','Australia/Sydney');
+insert into ml_timezones (countrycode,id,name) values ('AU','31','Australia/Broken_Hill');
+insert into ml_timezones (countrycode,id,name) values ('AU','32','Australia/Brisbane');
+insert into ml_timezones (countrycode,id,name) values ('AU','33','Australia/Lindeman');
+insert into ml_timezones (countrycode,id,name) values ('AU','34','Australia/Adelaide');
+insert into ml_timezones (countrycode,id,name) values ('AU','35','Australia/Darwin');
+insert into ml_timezones (countrycode,id,name) values ('AU','36','Australia/Perth');
+insert into ml_timezones (countrycode,id,name) values ('AW','37','America/Aruba');
+insert into ml_timezones (countrycode,id,name) values ('AZ','38','Asia/Baku');
+insert into ml_timezones (countrycode,id,name) values ('BA','39','Europe/Sarajevo');
+insert into ml_timezones (countrycode,id,name) values ('BB','40','America/Barbados');
+insert into ml_timezones (countrycode,id,name) values ('BD','41','Asia/Dhaka');
+insert into ml_timezones (countrycode,id,name) values ('BE','42','Europe/Brussels');
+insert into ml_timezones (countrycode,id,name) values ('BF','43','Africa/Ouagadougou');
+insert into ml_timezones (countrycode,id,name) values ('BG','44','Europe/Sofia');
+insert into ml_timezones (countrycode,id,name) values ('BH','45','Asia/Bahrain');
+insert into ml_timezones (countrycode,id,name) values ('BI','46','Africa/Bujumbura');
+insert into ml_timezones (countrycode,id,name) values ('BJ','47','Africa/Porto-Novo');
+insert into ml_timezones (countrycode,id,name) values ('BM','48','Atlantic/Bermuda');
+insert into ml_timezones (countrycode,id,name) values ('BN','49','Asia/Brunei');
+insert into ml_timezones (countrycode,id,name) values ('BO','50','America/La_Paz');
+insert into ml_timezones (countrycode,id,name) values ('BR','51','America/Noronha');
+insert into ml_timezones (countrycode,id,name) values ('BR','52','America/Belem');
+insert into ml_timezones (countrycode,id,name) values ('BR','53','America/Fortaleza');
+insert into ml_timezones (countrycode,id,name) values ('BR','54','America/Recife');
+insert into ml_timezones (countrycode,id,name) values ('BR','55','America/Araguaina');
+insert into ml_timezones (countrycode,id,name) values ('BR','56','America/Maceio');
+insert into ml_timezones (countrycode,id,name) values ('BR','57','America/Sao_Paulo');
+insert into ml_timezones (countrycode,id,name) values ('BR','58','America/Cuiaba');
+insert into ml_timezones (countrycode,id,name) values ('BR','59','America/Porto_Velho');
+insert into ml_timezones (countrycode,id,name) values ('BR','60','America/Boa_Vista');
+insert into ml_timezones (countrycode,id,name) values ('BR','61','America/Manaus');
+insert into ml_timezones (countrycode,id,name) values ('BR','62','America/Eirunepe');
+insert into ml_timezones (countrycode,id,name) values ('BR','63','America/Rio_Branco');
+insert into ml_timezones (countrycode,id,name) values ('BS','64','America/Nassau');
+insert into ml_timezones (countrycode,id,name) values ('BT','65','Asia/Thimphu');
+insert into ml_timezones (countrycode,id,name) values ('BW','66','Africa/Gaborone');
+insert into ml_timezones (countrycode,id,name) values ('BY','67','Europe/Minsk');
+insert into ml_timezones (countrycode,id,name) values ('BZ','68','America/Belize');
+insert into ml_timezones (countrycode,id,name) values ('CA','69','America/St_Johns');
+insert into ml_timezones (countrycode,id,name) values ('CA','70','America/Halifax');
+insert into ml_timezones (countrycode,id,name) values ('CA','71','America/Glace_Bay');
+insert into ml_timezones (countrycode,id,name) values ('CA','72','America/Goose_Bay');
+insert into ml_timezones (countrycode,id,name) values ('CA','73','America/Montreal');
+insert into ml_timezones (countrycode,id,name) values ('CA','74','America/Nipigon');
+insert into ml_timezones (countrycode,id,name) values ('CA','75','America/Thunder_Bay');
+insert into ml_timezones (countrycode,id,name) values ('CA','76','America/Pangnirtung');
+insert into ml_timezones (countrycode,id,name) values ('CA','77','America/Iqaluit');
+insert into ml_timezones (countrycode,id,name) values ('CA','78','America/Rankin_Inlet');
+insert into ml_timezones (countrycode,id,name) values ('CA','79','America/Winnipeg');
+insert into ml_timezones (countrycode,id,name) values ('CA','80','America/Rainy_River');
+insert into ml_timezones (countrycode,id,name) values ('CA','81','America/Cambridge_Bay');
+insert into ml_timezones (countrycode,id,name) values ('CA','82','America/Regina');
+insert into ml_timezones (countrycode,id,name) values ('CA','83','America/Swift_Current');
+insert into ml_timezones (countrycode,id,name) values ('CA','84','America/Edmonton');
+insert into ml_timezones (countrycode,id,name) values ('CA','85','America/Yellowknife');
+insert into ml_timezones (countrycode,id,name) values ('CA','86','America/Inuvik');
+insert into ml_timezones (countrycode,id,name) values ('CA','87','America/Dawson_Creek');
+insert into ml_timezones (countrycode,id,name) values ('CA','88','America/Vancouver');
+insert into ml_timezones (countrycode,id,name) values ('CA','89','America/Whitehorse');
+insert into ml_timezones (countrycode,id,name) values ('CA','90','America/Dawson');
+insert into ml_timezones (countrycode,id,name) values ('CC','91','Indian/Cocos');
+insert into ml_timezones (countrycode,id,name) values ('CD','92','Africa/Kinshasa');
+insert into ml_timezones (countrycode,id,name) values ('CD','93','Africa/Lubumbashi');
+insert into ml_timezones (countrycode,id,name) values ('CF','94','Africa/Bangui');
+insert into ml_timezones (countrycode,id,name) values ('CG','95','Africa/Brazzaville');
+insert into ml_timezones (countrycode,id,name) values ('CH','96','Europe/Zurich');
+insert into ml_timezones (countrycode,id,name) values ('CI','97','Africa/Abidjan');
+insert into ml_timezones (countrycode,id,name) values ('CK','98','Pacific/Rarotonga');
+insert into ml_timezones (countrycode,id,name) values ('CL','99','America/Santiago');
+insert into ml_timezones (countrycode,id,name) values ('CL','100','Pacific/Easter');
+insert into ml_timezones (countrycode,id,name) values ('CM','101','Africa/Douala');
+insert into ml_timezones (countrycode,id,name) values ('CN','102','Asia/Harbin');
+insert into ml_timezones (countrycode,id,name) values ('CN','103','Asia/Shanghai');
+insert into ml_timezones (countrycode,id,name) values ('CN','104','Asia/Chungking');
+insert into ml_timezones (countrycode,id,name) values ('CN','105','Asia/Urumqi');
+insert into ml_timezones (countrycode,id,name) values ('CN','106','Asia/Kashgar');
+insert into ml_timezones (countrycode,id,name) values ('CO','107','America/Bogota');
+insert into ml_timezones (countrycode,id,name) values ('CR','108','America/Costa_Rica');
+insert into ml_timezones (countrycode,id,name) values ('CU','109','America/Havana');
+insert into ml_timezones (countrycode,id,name) values ('CV','110','Atlantic/Cape_Verde');
+insert into ml_timezones (countrycode,id,name) values ('CX','111','Indian/Christmas');
+insert into ml_timezones (countrycode,id,name) values ('CY','112','Asia/Nicosia');
+insert into ml_timezones (countrycode,id,name) values ('CZ','113','Europe/Prague');
+insert into ml_timezones (countrycode,id,name) values ('DE','114','Europe/Berlin');
+insert into ml_timezones (countrycode,id,name) values ('DJ','115','Africa/Djibouti');
+insert into ml_timezones (countrycode,id,name) values ('DK','116','Europe/Copenhagen');
+insert into ml_timezones (countrycode,id,name) values ('DM','117','America/Dominica');
+insert into ml_timezones (countrycode,id,name) values ('DO','118','America/Santo_Domingo');
+insert into ml_timezones (countrycode,id,name) values ('DZ','119','Africa/Algiers');
+insert into ml_timezones (countrycode,id,name) values ('EC','120','America/Guayaquil');
+insert into ml_timezones (countrycode,id,name) values ('EC','121','Pacific/Galapagos');
+insert into ml_timezones (countrycode,id,name) values ('EE','122','Europe/Tallinn');
+insert into ml_timezones (countrycode,id,name) values ('EG','123','Africa/Cairo');
+insert into ml_timezones (countrycode,id,name) values ('EH','124','Africa/El_Aaiun');
+insert into ml_timezones (countrycode,id,name) values ('ER','125','Africa/Asmera');
+insert into ml_timezones (countrycode,id,name) values ('ES','126','Europe/Madrid');
+insert into ml_timezones (countrycode,id,name) values ('ES','127','Africa/Ceuta');
+insert into ml_timezones (countrycode,id,name) values ('ES','128','Atlantic/Canary');
+insert into ml_timezones (countrycode,id,name) values ('ET','129','Africa/Addis_Ababa');
+insert into ml_timezones (countrycode,id,name) values ('FI','130','Europe/Helsinki');
+insert into ml_timezones (countrycode,id,name) values ('FJ','131','Pacific/Fiji');
+insert into ml_timezones (countrycode,id,name) values ('FK','132','Atlantic/Stanley');
+insert into ml_timezones (countrycode,id,name) values ('FM','133','Pacific/Yap');
+insert into ml_timezones (countrycode,id,name) values ('FM','134','Pacific/Truk');
+insert into ml_timezones (countrycode,id,name) values ('FM','135','Pacific/Ponape');
+insert into ml_timezones (countrycode,id,name) values ('FM','136','Pacific/Kosrae');
+insert into ml_timezones (countrycode,id,name) values ('FO','137','Atlantic/Faeroe');
+insert into ml_timezones (countrycode,id,name) values ('FR','138','Europe/Paris');
+insert into ml_timezones (countrycode,id,name) values ('GA','139','Africa/Libreville');
+insert into ml_timezones (countrycode,id,name) values ('GB','140','Europe/London');
+insert into ml_timezones (countrycode,id,name) values ('GB','141','Europe/Belfast');
+insert into ml_timezones (countrycode,id,name) values ('GD','142','America/Grenada');
+insert into ml_timezones (countrycode,id,name) values ('GE','143','Asia/Tbilisi');
+insert into ml_timezones (countrycode,id,name) values ('GF','144','America/Cayenne');
+insert into ml_timezones (countrycode,id,name) values ('GH','145','Africa/Accra');
+insert into ml_timezones (countrycode,id,name) values ('GI','146','Europe/Gibraltar');
+insert into ml_timezones (countrycode,id,name) values ('GL','147','America/Scoresbysund');
+insert into ml_timezones (countrycode,id,name) values ('GL','148','America/Godthab');
+insert into ml_timezones (countrycode,id,name) values ('GL','149','America/Thule');
+insert into ml_timezones (countrycode,id,name) values ('GM','150','Africa/Banjul');
+insert into ml_timezones (countrycode,id,name) values ('GN','151','Africa/Conakry');
+insert into ml_timezones (countrycode,id,name) values ('GP','152','America/Guadeloupe');
+insert into ml_timezones (countrycode,id,name) values ('GQ','153','Africa/Malabo');
+insert into ml_timezones (countrycode,id,name) values ('GR','154','Europe/Athens');
+insert into ml_timezones (countrycode,id,name) values ('GS','155','Atlantic/South_Georgia');
+insert into ml_timezones (countrycode,id,name) values ('GT','156','America/Guatemala');
+insert into ml_timezones (countrycode,id,name) values ('GU','157','Pacific/Guam');
+insert into ml_timezones (countrycode,id,name) values ('GW','158','Africa/Bissau');
+insert into ml_timezones (countrycode,id,name) values ('GY','159','America/Guyana');
+insert into ml_timezones (countrycode,id,name) values ('HK','160','Asia/Hong_Kong');
+insert into ml_timezones (countrycode,id,name) values ('HN','161','America/Tegucigalpa');
+insert into ml_timezones (countrycode,id,name) values ('HR','162','Europe/Zagreb');
+insert into ml_timezones (countrycode,id,name) values ('HT','163','America/Port-au-Prince');
+insert into ml_timezones (countrycode,id,name) values ('HU','164','Europe/Budapest');
+insert into ml_timezones (countrycode,id,name) values ('ID','165','Asia/Jakarta');
+insert into ml_timezones (countrycode,id,name) values ('ID','166','Asia/Pontianak');
+insert into ml_timezones (countrycode,id,name) values ('ID','167','Asia/Ujung_Pandang');
+insert into ml_timezones (countrycode,id,name) values ('ID','168','Asia/Jayapura');
+insert into ml_timezones (countrycode,id,name) values ('IE','169','Europe/Dublin');
+insert into ml_timezones (countrycode,id,name) values ('IL','170','Asia/Jerusalem');
+insert into ml_timezones (countrycode,id,name) values ('IN','171','Asia/Calcutta');
+insert into ml_timezones (countrycode,id,name) values ('IO','172','Indian/Chagos');
+insert into ml_timezones (countrycode,id,name) values ('IQ','173','Asia/Baghdad');
+insert into ml_timezones (countrycode,id,name) values ('IR','174','Asia/Tehran');
+insert into ml_timezones (countrycode,id,name) values ('IS','175','Atlantic/Reykjavik');
+insert into ml_timezones (countrycode,id,name) values ('IT','176','Europe/Rome');
+insert into ml_timezones (countrycode,id,name) values ('JM','177','America/Jamaica');
+insert into ml_timezones (countrycode,id,name) values ('JO','178','Asia/Amman');
+insert into ml_timezones (countrycode,id,name) values ('JP','179','Asia/Tokyo');
+insert into ml_timezones (countrycode,id,name) values ('KE','180','Africa/Nairobi');
+insert into ml_timezones (countrycode,id,name) values ('KG','181','Asia/Bishkek');
+insert into ml_timezones (countrycode,id,name) values ('KH','182','Asia/Phnom_Penh');
+insert into ml_timezones (countrycode,id,name) values ('KI','183','Pacific/Tarawa');
+insert into ml_timezones (countrycode,id,name) values ('KI','184','Pacific/Enderbury');
+insert into ml_timezones (countrycode,id,name) values ('KI','185','Pacific/Kiritimati');
+insert into ml_timezones (countrycode,id,name) values ('KM','186','Indian/Comoro');
+insert into ml_timezones (countrycode,id,name) values ('KN','187','America/St_Kitts');
+insert into ml_timezones (countrycode,id,name) values ('KP','188','Asia/Pyongyang');
+insert into ml_timezones (countrycode,id,name) values ('KR','189','Asia/Seoul');
+insert into ml_timezones (countrycode,id,name) values ('KW','190','Asia/Kuwait');
+insert into ml_timezones (countrycode,id,name) values ('KY','191','America/Cayman');
+insert into ml_timezones (countrycode,id,name) values ('KZ','192','Asia/Almaty');
+insert into ml_timezones (countrycode,id,name) values ('KZ','193','Asia/Aqtobe');
+insert into ml_timezones (countrycode,id,name) values ('KZ','194','Asia/Aqtau');
+insert into ml_timezones (countrycode,id,name) values ('LA','195','Asia/Vientiane');
+insert into ml_timezones (countrycode,id,name) values ('LB','196','Asia/Beirut');
+insert into ml_timezones (countrycode,id,name) values ('LC','197','America/St_Lucia');
+insert into ml_timezones (countrycode,id,name) values ('LI','198','Europe/Vaduz');
+insert into ml_timezones (countrycode,id,name) values ('LK','199','Asia/Colombo');
+insert into ml_timezones (countrycode,id,name) values ('LR','200','Africa/Monrovia');
+insert into ml_timezones (countrycode,id,name) values ('LS','201','Africa/Maseru');
+insert into ml_timezones (countrycode,id,name) values ('LT','202','Europe/Vilnius');
+insert into ml_timezones (countrycode,id,name) values ('LU','203','Europe/Luxembourg');
+insert into ml_timezones (countrycode,id,name) values ('LV','204','Europe/Riga');
+insert into ml_timezones (countrycode,id,name) values ('LY','205','Africa/Tripoli');
+insert into ml_timezones (countrycode,id,name) values ('MA','206','Africa/Casablanca');
+insert into ml_timezones (countrycode,id,name) values ('MC','207','Europe/Monaco');
+insert into ml_timezones (countrycode,id,name) values ('MD','208','Europe/Chisinau');
+insert into ml_timezones (countrycode,id,name) values ('MG','209','Indian/Antananarivo');
+insert into ml_timezones (countrycode,id,name) values ('MH','210','Pacific/Majuro');
+insert into ml_timezones (countrycode,id,name) values ('MH','211','Pacific/Kwajalein');
+insert into ml_timezones (countrycode,id,name) values ('MK','212','Europe/Skopje');
+insert into ml_timezones (countrycode,id,name) values ('ML','213','Africa/Bamako');
+insert into ml_timezones (countrycode,id,name) values ('ML','214','Africa/Timbuktu');
+insert into ml_timezones (countrycode,id,name) values ('MM','215','Asia/Rangoon');
+insert into ml_timezones (countrycode,id,name) values ('MN','216','Asia/Ulaanbaatar');
+insert into ml_timezones (countrycode,id,name) values ('MN','217','Asia/Hovd');
+insert into ml_timezones (countrycode,id,name) values ('MO','218','Asia/Macao');
+insert into ml_timezones (countrycode,id,name) values ('MP','219','Pacific/Saipan');
+insert into ml_timezones (countrycode,id,name) values ('MQ','220','America/Martinique');
+insert into ml_timezones (countrycode,id,name) values ('MR','221','Africa/Nouakchott');
+insert into ml_timezones (countrycode,id,name) values ('MS','222','America/Montserrat');
+insert into ml_timezones (countrycode,id,name) values ('MT','223','Europe/Malta');
+insert into ml_timezones (countrycode,id,name) values ('MU','224','Indian/Mauritius');
+insert into ml_timezones (countrycode,id,name) values ('MV','225','Indian/Maldives');
+insert into ml_timezones (countrycode,id,name) values ('MW','226','Africa/Blantyre');
+insert into ml_timezones (countrycode,id,name) values ('MX','227','America/Mexico_City');
+insert into ml_timezones (countrycode,id,name) values ('MX','228','America/Cancun');
+insert into ml_timezones (countrycode,id,name) values ('MX','229','America/Merida');
+insert into ml_timezones (countrycode,id,name) values ('MX','230','America/Monterrey');
+insert into ml_timezones (countrycode,id,name) values ('MX','231','America/Mazatlan');
+insert into ml_timezones (countrycode,id,name) values ('MX','232','America/Chihuahua');
+insert into ml_timezones (countrycode,id,name) values ('MX','233','America/Hermosillo');
+insert into ml_timezones (countrycode,id,name) values ('MX','234','America/Tijuana');
+insert into ml_timezones (countrycode,id,name) values ('MY','235','Asia/Kuala_Lumpur');
+insert into ml_timezones (countrycode,id,name) values ('MY','236','Asia/Kuching');
+insert into ml_timezones (countrycode,id,name) values ('MZ','237','Africa/Maputo');
+insert into ml_timezones (countrycode,id,name) values ('NA','238','Africa/Windhoek');
+insert into ml_timezones (countrycode,id,name) values ('NC','239','Pacific/Noumea');
+insert into ml_timezones (countrycode,id,name) values ('NE','240','Africa/Niamey');
+insert into ml_timezones (countrycode,id,name) values ('NF','241','Pacific/Norfolk');
+insert into ml_timezones (countrycode,id,name) values ('NG','242','Africa/Lagos');
+insert into ml_timezones (countrycode,id,name) values ('NI','243','America/Managua');
+insert into ml_timezones (countrycode,id,name) values ('NL','244','Europe/Amsterdam');
+insert into ml_timezones (countrycode,id,name) values ('NO','245','Europe/Oslo');
+insert into ml_timezones (countrycode,id,name) values ('NP','246','Asia/Katmandu');
+insert into ml_timezones (countrycode,id,name) values ('NR','247','Pacific/Nauru');
+insert into ml_timezones (countrycode,id,name) values ('NU','248','Pacific/Niue');
+insert into ml_timezones (countrycode,id,name) values ('NZ','249','Pacific/Auckland');
+insert into ml_timezones (countrycode,id,name) values ('NZ','250','Pacific/Chatham');
+insert into ml_timezones (countrycode,id,name) values ('OM','251','Asia/Muscat');
+insert into ml_timezones (countrycode,id,name) values ('PA','252','America/Panama');
+insert into ml_timezones (countrycode,id,name) values ('PE','253','America/Lima');
+insert into ml_timezones (countrycode,id,name) values ('PF','254','Pacific/Tahiti');
+insert into ml_timezones (countrycode,id,name) values ('PF','255','Pacific/Marquesas');
+insert into ml_timezones (countrycode,id,name) values ('PF','256','Pacific/Gambier');
+insert into ml_timezones (countrycode,id,name) values ('PG','257','Pacific/Port_Moresby');
+insert into ml_timezones (countrycode,id,name) values ('PH','258','Asia/Manila');
+insert into ml_timezones (countrycode,id,name) values ('PK','259','Asia/Karachi');
+insert into ml_timezones (countrycode,id,name) values ('PL','260','Europe/Warsaw');
+insert into ml_timezones (countrycode,id,name) values ('PM','261','America/Miquelon');
+insert into ml_timezones (countrycode,id,name) values ('PN','262','Pacific/Pitcairn');
+insert into ml_timezones (countrycode,id,name) values ('PR','263','America/Puerto_Rico');
+insert into ml_timezones (countrycode,id,name) values ('PS','264','Asia/Gaza');
+insert into ml_timezones (countrycode,id,name) values ('PT','265','Europe/Lisbon');
+insert into ml_timezones (countrycode,id,name) values ('PT','266','Atlantic/Madeira');
+insert into ml_timezones (countrycode,id,name) values ('PT','267','Atlantic/Azores');
+insert into ml_timezones (countrycode,id,name) values ('PW','268','Pacific/Palau');
+insert into ml_timezones (countrycode,id,name) values ('PY','269','America/Asuncion');
+insert into ml_timezones (countrycode,id,name) values ('QA','270','Asia/Qatar');
+insert into ml_timezones (countrycode,id,name) values ('RE','271','Indian/Reunion');
+insert into ml_timezones (countrycode,id,name) values ('RO','272','Europe/Bucharest');
+insert into ml_timezones (countrycode,id,name) values ('RU','273','Europe/Kaliningrad');
+insert into ml_timezones (countrycode,id,name) values ('RU','274','Europe/Moscow');
+insert into ml_timezones (countrycode,id,name) values ('RU','275','Europe/Samara');
+insert into ml_timezones (countrycode,id,name) values ('RU','276','Asia/Yekaterinburg');
+insert into ml_timezones (countrycode,id,name) values ('RU','277','Asia/Omsk');
+insert into ml_timezones (countrycode,id,name) values ('RU','278','Asia/Novosibirsk');
+insert into ml_timezones (countrycode,id,name) values ('RU','279','Asia/Krasnoyarsk');
+insert into ml_timezones (countrycode,id,name) values ('RU','280','Asia/Irkutsk');
+insert into ml_timezones (countrycode,id,name) values ('RU','281','Asia/Yakutsk');
+insert into ml_timezones (countrycode,id,name) values ('RU','282','Asia/Vladivostok');
+insert into ml_timezones (countrycode,id,name) values ('RU','283','Asia/Magadan');
+insert into ml_timezones (countrycode,id,name) values ('RU','284','Asia/Kamchatka');
+insert into ml_timezones (countrycode,id,name) values ('RU','285','Asia/Anadyr');
+insert into ml_timezones (countrycode,id,name) values ('RW','286','Africa/Kigali');
+insert into ml_timezones (countrycode,id,name) values ('SA','287','Asia/Riyadh');
+insert into ml_timezones (countrycode,id,name) values ('SB','288','Pacific/Guadalcanal');
+insert into ml_timezones (countrycode,id,name) values ('SC','289','Indian/Mahe');
+insert into ml_timezones (countrycode,id,name) values ('SD','290','Africa/Khartoum');
+insert into ml_timezones (countrycode,id,name) values ('SE','291','Europe/Stockholm');
+insert into ml_timezones (countrycode,id,name) values ('SG','292','Asia/Singapore');
+insert into ml_timezones (countrycode,id,name) values ('SH','293','Atlantic/St_Helena');
+insert into ml_timezones (countrycode,id,name) values ('SI','294','Europe/Ljubljana');
+insert into ml_timezones (countrycode,id,name) values ('SJ','295','Arctic/Longyearbyen');
+insert into ml_timezones (countrycode,id,name) values ('SJ','296','Atlantic/Jan_Mayen');
+insert into ml_timezones (countrycode,id,name) values ('SK','297','Europe/Bratislava');
+insert into ml_timezones (countrycode,id,name) values ('SL','298','Africa/Freetown');
+insert into ml_timezones (countrycode,id,name) values ('SM','299','Europe/San_Marino');
+insert into ml_timezones (countrycode,id,name) values ('SN','300','Africa/Dakar');
+insert into ml_timezones (countrycode,id,name) values ('SO','301','Africa/Mogadishu');
+insert into ml_timezones (countrycode,id,name) values ('SR','302','America/Paramaribo');
+insert into ml_timezones (countrycode,id,name) values ('ST','303','Africa/Sao_Tome');
+insert into ml_timezones (countrycode,id,name) values ('SV','304','America/El_Salvador');
+insert into ml_timezones (countrycode,id,name) values ('SY','305','Asia/Damascus');
+insert into ml_timezones (countrycode,id,name) values ('SZ','306','Africa/Mbabane');
+insert into ml_timezones (countrycode,id,name) values ('TC','307','America/Grand_Turk');
+insert into ml_timezones (countrycode,id,name) values ('TD','308','Africa/Ndjamena');
+insert into ml_timezones (countrycode,id,name) values ('TF','309','Indian/Kerguelen');
+insert into ml_timezones (countrycode,id,name) values ('TG','310','Africa/Lome');
+insert into ml_timezones (countrycode,id,name) values ('TH','311','Asia/Bangkok');
+insert into ml_timezones (countrycode,id,name) values ('TJ','312','Asia/Dushanbe');
+insert into ml_timezones (countrycode,id,name) values ('TK','313','Pacific/Fakaofo');
+insert into ml_timezones (countrycode,id,name) values ('TM','314','Asia/Ashgabat');
+insert into ml_timezones (countrycode,id,name) values ('TN','315','Africa/Tunis');
+insert into ml_timezones (countrycode,id,name) values ('TO','316','Pacific/Tongatapu');
+insert into ml_timezones (countrycode,id,name) values ('TP','317','Asia/Dili');
+insert into ml_timezones (countrycode,id,name) values ('TR','318','Europe/Istanbul');
+insert into ml_timezones (countrycode,id,name) values ('TT','319','America/Port_of_Spain');
+insert into ml_timezones (countrycode,id,name) values ('TV','320','Pacific/Funafuti');
+insert into ml_timezones (countrycode,id,name) values ('TW','321','Asia/Taipei');
+insert into ml_timezones (countrycode,id,name) values ('TZ','322','Africa/Dar_es_Salaam');
+insert into ml_timezones (countrycode,id,name) values ('UA','323','Europe/Kiev');
+insert into ml_timezones (countrycode,id,name) values ('UA','324','Europe/Uzhgorod');
+insert into ml_timezones (countrycode,id,name) values ('UA','325','Europe/Zaporozhye');
+insert into ml_timezones (countrycode,id,name) values ('UA','326','Europe/Simferopol');
+insert into ml_timezones (countrycode,id,name) values ('UG','327','Africa/Kampala');
+insert into ml_timezones (countrycode,id,name) values ('UM','328','Pacific/Johnston');
+insert into ml_timezones (countrycode,id,name) values ('UM','329','Pacific/Midway');
+insert into ml_timezones (countrycode,id,name) values ('UM','330','Pacific/Wake');
+insert into ml_timezones (countrycode,id,name) values ('US','331','America/New_York');
+insert into ml_timezones (countrycode,id,name) values ('US','332','America/Detroit');
+insert into ml_timezones (countrycode,id,name) values ('US','333','America/Louisville');
+insert into ml_timezones (countrycode,id,name) values ('US','334','America/Kentucky/Monticello');
+insert into ml_timezones (countrycode,id,name) values ('US','335','America/Indianapolis');
+insert into ml_timezones (countrycode,id,name) values ('US','336','America/Indiana/Marengo');
+insert into ml_timezones (countrycode,id,name) values ('US','337','America/Indiana/Knox');
+insert into ml_timezones (countrycode,id,name) values ('US','338','America/Indiana/Vevay');
+insert into ml_timezones (countrycode,id,name) values ('US','339','America/Chicago');
+insert into ml_timezones (countrycode,id,name) values ('US','340','America/Menominee');
+insert into ml_timezones (countrycode,id,name) values ('US','341','America/Denver');
+insert into ml_timezones (countrycode,id,name) values ('US','342','America/Boise');
+insert into ml_timezones (countrycode,id,name) values ('US','343','America/Shiprock');
+insert into ml_timezones (countrycode,id,name) values ('US','344','America/Phoenix');
+insert into ml_timezones (countrycode,id,name) values ('US','345','America/Los_Angeles');
+insert into ml_timezones (countrycode,id,name) values ('US','346','America/Anchorage');
+insert into ml_timezones (countrycode,id,name) values ('US','347','America/Juneau');
+insert into ml_timezones (countrycode,id,name) values ('US','348','America/Yakutat');
+insert into ml_timezones (countrycode,id,name) values ('US','349','America/Nome');
+insert into ml_timezones (countrycode,id,name) values ('US','350','America/Adak');
+insert into ml_timezones (countrycode,id,name) values ('US','351','Pacific/Honolulu');
+insert into ml_timezones (countrycode,id,name) values ('UY','352','America/Montevideo');
+insert into ml_timezones (countrycode,id,name) values ('UZ','353','Asia/Samarkand');
+insert into ml_timezones (countrycode,id,name) values ('UZ','354','Asia/Tashkent');
+insert into ml_timezones (countrycode,id,name) values ('VA','355','Europe/Vatican');
+insert into ml_timezones (countrycode,id,name) values ('VC','356','America/St_Vincent');
+insert into ml_timezones (countrycode,id,name) values ('VE','357','America/Caracas');
+insert into ml_timezones (countrycode,id,name) values ('VG','358','America/Tortola');
+insert into ml_timezones (countrycode,id,name) values ('VI','359','America/St_Thomas');
+insert into ml_timezones (countrycode,id,name) values ('VN','360','Asia/Saigon');
+insert into ml_timezones (countrycode,id,name) values ('VU','361','Pacific/Efate');
+insert into ml_timezones (countrycode,id,name) values ('WF','362','Pacific/Wallis');
+insert into ml_timezones (countrycode,id,name) values ('WS','363','Pacific/Apia');
+insert into ml_timezones (countrycode,id,name) values ('YE','364','Asia/Aden');
+insert into ml_timezones (countrycode,id,name) values ('YT','365','Indian/Mayotte');
+insert into ml_timezones (countrycode,id,name) values ('YU','366','Europe/Belgrade');
+insert into ml_timezones (countrycode,id,name) values ('ZA','367','Africa/Johannesburg');
+insert into ml_timezones (countrycode,id,name) values ('ZM','368','Africa/Lusaka');
+insert into ml_timezones (countrycode,id,name) values ('ZW','369','Africa/Harare');
+
+commit work;
diff --git a/sql/monolith_core_drop.sql b/sql/monolith_core_drop.sql
new file mode 100644 (file)
index 0000000..8b5c3d4
--- /dev/null
@@ -0,0 +1,24 @@
+-- Drop the core monolith schema
+-- - by Richard W.M. Jones <rich@annexia.org>
+--
+-- This library is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU Library General Public
+-- License as published by the Free Software Foundation; either
+-- version 2 of the License, or (at your option) any later version.
+--
+-- This library is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+-- Library General Public License for more details.
+--
+-- You should have received a copy of the GNU Library General Public
+-- License along with this library; if not, write to the Free
+-- Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+--
+-- $Id: monolith_core_drop.sql,v 1.1 2002/10/19 16:09:31 rich Exp $
+
+drop index ml_timezones_name;
+drop table ml_timezones;
+
+drop index ml_countries_name;
+drop table ml_countries;
diff --git a/sql/monolith_resources_create.sql b/sql/monolith_resources_create.sql
new file mode 100644 (file)
index 0000000..9acc493
--- /dev/null
@@ -0,0 +1,39 @@
+-- Create schema for monolith resources.
+-- - by Richard W.M. Jones <rich@annexia.org>
+--
+-- This library is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU Library General Public
+-- License as published by the Free Software Foundation; either
+-- version 2 of the License, or (at your option) any later version.
+--
+-- This library is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+-- Library General Public License for more details.
+--
+-- You should have received a copy of the GNU Library General Public
+-- License along with this library; if not, write to the Free
+-- Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+--
+-- $Id: monolith_resources_create.sql,v 1.2 2002/11/15 20:46:05 rich Exp $
+--
+-- Depends: monolith_core
+
+begin work;
+
+create table ml_resources
+(
+       resid serial,
+       name text               -- Unique name for each resource
+               constraint ml_resources_name_nn
+               not null
+);
+
+create unique index ml_resources_name_ui on ml_resources (name);
+
+-- Grant access to the webserver.
+
+grant select, insert, update, delete on ml_resources to nobody;
+grant select, update on ml_resources_resid_seq to nobody;
+
+commit work;
diff --git a/sql/monolith_resources_drop.sql b/sql/monolith_resources_drop.sql
new file mode 100644 (file)
index 0000000..d8a12d7
--- /dev/null
@@ -0,0 +1,22 @@
+-- Drop schema for monolith resources.
+-- - by Richard W.M. Jones <rich@annexia.org>
+--
+-- This library is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU Library General Public
+-- License as published by the Free Software Foundation; either
+-- version 2 of the License, or (at your option) any later version.
+--
+-- This library is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+-- Library General Public License for more details.
+--
+-- You should have received a copy of the GNU Library General Public
+-- License along with this library; if not, write to the Free
+-- Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+--
+-- $Id: monolith_resources_drop.sql,v 1.1 2002/10/22 12:31:02 rich Exp $
+
+drop index ml_resources_name_ui;
+drop table ml_resources;
+drop sequence ml_resources_resid_seq;
diff --git a/sql/monolith_users_create.sql b/sql/monolith_users_create.sql
new file mode 100644 (file)
index 0000000..0c3f6b1
--- /dev/null
@@ -0,0 +1,79 @@
+-- Create schema for monolith users.
+-- - by Richard W.M. Jones <rich@annexia.org>
+--
+-- This library is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU Library General Public
+-- License as published by the Free Software Foundation; either
+-- version 2 of the License, or (at your option) any later version.
+--
+-- This library is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+-- Library General Public License for more details.
+--
+-- You should have received a copy of the GNU Library General Public
+-- License along with this library; if not, write to the Free
+-- Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+--
+-- $Id: monolith_users_create.sql,v 1.2 2002/11/15 20:46:05 rich Exp $
+--
+-- Depends: monolith_core
+
+-- Monolith does not require that you use this table. You can modify
+-- the schema in monolith_auth_create.sql to use your own users table
+-- if you wish. However, many of the monolith standard widgets use
+-- this table for accessing user information.
+
+begin work;
+
+create table ml_users
+(
+       userid serial,          -- Unique number for each user
+       email text              -- Unique email address for each user
+               constraint ml_users_email_nn
+               not null,
+       username varchar(32)    -- Displayed username (not necessarily unique)
+               constraint ml_users_username_nn
+               not null,
+       password varchar(32),   -- Hashed password (not always required)
+
+       -- Personal data.
+       given_name text,        -- Forename (in western locales)
+       family_name text,       -- Surname (in western locales)
+       date_of_birth date,     -- Date of birth
+       gender char(1)          -- Gender
+               constraint ml_users_gender_ck
+               check (gender in ('m', 'f')),
+
+       -- Locale information.
+       langcode char(8),       -- Language and modifiers
+       timezone int4           -- POSIX Timezone
+               references ml_timezones (id),
+       countrycode char(2)     -- ISO country code
+               references ml_countries (code),
+
+       -- Accounting information.
+       signup_date date        -- When the account was created
+               default current_date
+               constraint ml_users_signup_date_nn
+               not null,
+       lastlogin_date date,    -- When they last logged in
+       nr_logins int4          -- Number of times they have logged in
+               default 0
+               constraint ml_users_nr_logins_nn
+               not null,
+       bad_logins int4         -- Since they last logged in, how many bad
+                               -- login attempts have been made
+               default 0
+               constraint ml_users_bad_logins_nn
+               not null
+);
+
+create unique index ml_users_email_ui on ml_users (email);
+
+-- Grant access to the webserver.
+
+grant select, insert, update, delete on ml_users to nobody;
+grant select, update on ml_users_userid_seq to nobody;
+
+commit work;
diff --git a/sql/monolith_users_drop.sql b/sql/monolith_users_drop.sql
new file mode 100644 (file)
index 0000000..20c235a
--- /dev/null
@@ -0,0 +1,22 @@
+-- Drop schema for monolith users.
+-- - by Richard W.M. Jones <rich@annexia.org>
+--
+-- This library is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU Library General Public
+-- License as published by the Free Software Foundation; either
+-- version 2 of the License, or (at your option) any later version.
+--
+-- This library is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+-- Library General Public License for more details.
+--
+-- You should have received a copy of the GNU Library General Public
+-- License along with this library; if not, write to the Free
+-- Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+--
+-- $Id: monolith_users_drop.sql,v 1.2 2002/10/22 12:31:02 rich Exp $
+
+drop index ml_users_email_ui;
+drop table ml_users;
+drop sequence ml_users_userid_seq;
diff --git a/src/ml_box.c b/src/ml_box.c
new file mode 100644 (file)
index 0000000..f9bd41e
--- /dev/null
@@ -0,0 +1,72 @@
+/* Monolith box class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_box.c,v 1.2 2002/09/14 14:35:58 rich Exp $
+ */
+
+#include "config.h"
+
+#include <pthr_iolib.h>
+
+#include "ml_widget.h"
+#include "monolith.h"
+#include "ml_box.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations box_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_box
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_widget w;                 /* Widget packed inside the box. */
+};
+
+ml_box
+new_ml_box (pool pool)
+{
+  ml_box w = pmalloc (pool, sizeof *w);
+
+  w->ops = &box_ops;
+  w->pool = pool;
+  w->w = 0;
+
+  return w;
+}
+
+void
+ml_box_pack (ml_box w, ml_widget _w)
+{
+  w->w = _w;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_box w = (ml_box) vw;
+
+  io_fprintf (io, "<span class=\"ml_box\">");
+
+  if (w->w)
+    ml_widget_repaint (w->w, session, windowid, io);
+
+  io_fprintf (io, "</span>");
+}
diff --git a/src/ml_box.h b/src/ml_box.h
new file mode 100644 (file)
index 0000000..f6f4e21
--- /dev/null
@@ -0,0 +1,44 @@
+/* Monolith box class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_box.h,v 1.1 2002/08/28 19:50:27 rich Exp $
+ */
+
+#ifndef ML_BOX_H
+#define ML_BOX_H
+
+#include <ml_widget.h>
+
+struct ml_box;
+typedef struct ml_box *ml_box;
+
+/* Function: new_ml_box - monolith box widget
+ * Function: ml_box_pack
+ *
+ * The monolith box widget encloses another widget inside a rectangular
+ * outline.
+ *
+ * @code{new_ml_box} creates a new box widget.
+ *
+ * @code{ml_box_pack} packs another widget inside the box. A maximum of
+ * one widget can be packed inside a box, so if you call this function
+ * multiple times, then earlier widgets will be forgotten.
+ */
+extern ml_box new_ml_box (pool pool);
+extern void ml_box_pack (ml_box, ml_widget);
+
+#endif /* ML_BOX_H */
diff --git a/src/ml_button.c b/src/ml_button.c
new file mode 100644 (file)
index 0000000..468063c
--- /dev/null
@@ -0,0 +1,200 @@
+/* Monolith button class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_button.c,v 1.8 2003/01/11 16:39:09 rich Exp $
+ */
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_smarttext.h"
+#include "ml_button.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property properties[];
+
+struct ml_widget_operations button_ops =
+  {
+    repaint: repaint,
+    properties: properties,
+  };
+
+struct ml_button
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  const char *text;            /* HTML printed for the button. */
+  const char *title;           /* Tool tip (NULL for none). */
+  const char *clazz;           /* Class (NULL for default). */
+  const char *style;           /* Style: default, key, compact, link */
+  const char *colour;          /* Button colour (NULL for default). */
+  const char *action_id;       /* Action. */
+  const char *target;          /* Button target (NULL for no popup). */
+  int popup_w, popup_h;                /* Popup window size. */
+};
+
+static struct ml_widget_property properties[] =
+  {
+    { name: "text",
+      offset: ml_offsetof (struct ml_button, text),
+      type: ML_PROP_STRING },
+    { name: "title",
+      offset: ml_offsetof (struct ml_button, title),
+      type: ML_PROP_STRING },
+    { name: "button.style",
+      offset: ml_offsetof (struct ml_button, style),
+      type: ML_PROP_STRING },
+    { name: "color",
+      offset: ml_offsetof (struct ml_button, colour),
+      type: ML_PROP_STRING },
+    { name: "class",
+      offset: ml_offsetof (struct ml_button, clazz),
+      type: ML_PROP_STRING },
+    { 0 }
+  };
+
+ml_button
+new_ml_button (pool pool, const char *text)
+{
+  ml_button w = pmalloc (pool, sizeof *w);
+
+  w->ops = &button_ops;
+  w->pool = pool;
+  w->text = text;
+  w->title = 0;
+  w->clazz = 0;
+  w->style = 0;
+  w->colour = 0;
+  w->action_id = 0;
+  w->target = 0;
+  w->popup_w = w->popup_h = 0;
+
+  return w;
+}
+
+void
+ml_button_set_callback (ml_button w,
+                       void (*fn)(ml_session, void *),
+                       ml_session session, void *data)
+{
+  if (w->action_id)
+    ml_unregister_action (session, w->action_id);
+  w->action_id = 0;
+
+  if (fn)
+    w->action_id = ml_register_action (session, fn, data);
+}
+
+void
+ml_button_set_popup (ml_button w, const char *name)
+{
+  w->target = name;
+}
+
+void
+ml_button_set_popup_size (ml_button w, int width, int height)
+{
+  w->popup_w = width;
+  w->popup_h = height;
+}
+
+static inline const char *
+get_class (ml_button w)
+{
+  if (!w->clazz)
+    {
+      if (w->style == 0 || strcmp (w->style, "default") == 0)
+       return "ml_button";
+      else if (strcmp (w->style, "key") == 0)
+       return "ml_button_key";
+      else if (strcmp (w->style, "compact") == 0)
+       return "ml_button_compact";
+      else if (strcmp (w->style, "link") == 0)
+       return "ml_button_link";
+      else
+       abort ();               /* Unknown button.style for button. */
+    }
+
+  return w->clazz;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_button w = (ml_button) vw;
+
+  if (w->text)
+    {
+      const char *clazz = get_class (w);
+
+      if (w->action_id)
+       {
+         pool tmp = new_subpool (w->pool);
+
+         /* XXX Link should not contain ml_window parameter if w->target
+          * is set.
+          */
+         const char *link =
+           psprintf (tmp, "%s?ml_action=%s&ml_window=%s",
+                     ml_session_script_name (session),
+                     w->action_id,
+                     windowid);
+
+         io_fprintf (io, "<a class=\"%s\" href=\"%s\"", clazz, link);
+
+         if (w->title)
+           {
+             io_fputs (" title=\"", io);
+             ml_plaintext_print (io, w->title);
+             io_fputc ('"', io);
+           }
+
+         if (w->colour)
+           io_fprintf (io, " style=\"color: %s\"", w->colour);
+
+         if (w->target)
+           {
+             io_fprintf (io, " target=\"%s\"", w->target);
+             if (w->popup_w != 0 && w->popup_h != 0)
+               {
+                 io_fprintf (io, " onclick=\"open ('%s', '%s', "
+                             "'width=%d,height=%d,scrollbars=1'); "
+                             "return false;\"",
+                             link, w->target, w->popup_w, w->popup_h);
+               }
+           }
+
+         if (!w->style || strcmp (w->style, "compact") != 0)
+           io_fprintf (io, ">%s</a>", w->text);
+         else
+           io_fprintf (io, ">[%s]</a>", w->text);
+
+         delete_pool (tmp);
+       }
+      else
+       io_fprintf (io, "<span class=\"%s\">%s</span>", clazz, w->text);
+    }
+}
diff --git a/src/ml_button.h b/src/ml_button.h
new file mode 100644 (file)
index 0000000..315fea7
--- /dev/null
@@ -0,0 +1,90 @@
+/* Monolith button class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_button.h,v 1.6 2002/11/08 23:19:14 rich Exp $
+ */
+
+#ifndef ML_BUTTON_H
+#define ML_BUTTON_H
+
+#include "monolith.h"
+
+struct ml_button;
+typedef struct ml_button *ml_button;
+
+/* Function: new_ml_button - monolith button widget
+ * Function: ml_button_set_callback
+ * Function: ml_button_set_popup
+ * Function: ml_button_set_popup_size
+ *
+ * A button widget is a simple button which calls a function when clicked.
+ *
+ * @code{new_ml_button} creates a new button widget.
+ *
+ * The following properties can be changed on buttons (see
+ * @ref{ml_widget_set_property(3)}):
+ *
+ * @code{text}: The text on the button. The string must have a
+ * lifetime as long as or longer than the button's pool.  Setting this
+ * to @code{NULL} has the same effect as setting it to the empty
+ * string, which is to say not very useful because the button cannot
+ * be pressed.
+ *
+ * @code{title}: If not @code{NULL}, then this string will be
+ * displayed as a tool tip (bubble) when the mouse is held over the
+ * button. Note that this sort of information hiding rarely scores
+ * highly in usability tests, so you should make sure important
+ * information is always visible in the interface.
+ *
+ * @code{button.style}: The style may be: @code{NULL} or @code{"default"}
+ * meaning the default look and feel; @code{"key"} meaning that the
+ * button has a fixed width of @code{1em}, resulting in a more
+ * pleasing effect for calculator key buttons; @code{"compact"}
+ * meaning a compact @code{[text]} look; @code{"link"} meaning that
+ * the button will look like a hyperlink.
+ *
+ * @code{color}: The button's color property.
+ *
+ * @code{class}: The stylesheet class.
+ *
+ * @code{ml_button_set_callback} updates the callback
+ * function which is invoked when the button is pressed.
+ *
+ * @code{ml_button_set_popup} sets the popup property of the
+ * button. When the user clicks on a popup button, a new popup
+ * window opens on the screen. To make a button into a popup
+ * button, the popup property should be a non-@code{NULL} string
+ * which is the name of the new browser window (note that because
+ * of limitations in HTML, browser window names are global, so
+ * it is recommended that names be "@code{appname}_@code{name}").
+ * To change a popup button back to an ordinary button, set the
+ * popup property to @code{NULL}.
+ *
+ * The callback used by a popup button must create a top-level
+ * window, otherwise you will get an internal server error.
+ *
+ * @code{ml_button_set_popup_size} changes the size of the popup
+ * window. The default is width 0, height 0, which usually creates a
+ * popup window which is the same size and shape as the current
+ * browser window.
+ */
+extern ml_button new_ml_button (pool pool, const char *text);
+extern void ml_button_set_callback (ml_button, void (*fn)(ml_session, void *), ml_session, void *);
+extern void ml_button_set_popup (ml_button, const char *name);
+extern void ml_button_set_popup_size (ml_button, int width, int height);
+
+#endif /* ML_BUTTON_H */
diff --git a/src/ml_close_button.c b/src/ml_close_button.c
new file mode 100644 (file)
index 0000000..1b83f36
--- /dev/null
@@ -0,0 +1,90 @@
+/* Monolith close button class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_close_button.c,v 1.3 2002/11/13 21:41:01 rich Exp $
+ */
+
+#include "config.h"
+
+#include <pool.h>
+#include <pstring.h>
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_close_button.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property properties[];
+
+struct ml_widget_operations close_button_ops =
+  {
+    repaint: repaint,
+    properties: properties,
+  };
+
+struct ml_close_button
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  const char *text;            /* HTML printed for the button. */
+  int reload_opener;           /* If set, reload the opener window. */
+};
+
+static struct ml_widget_property properties[] =
+  {
+    { name: "text",
+      offset: ml_offsetof (struct ml_close_button, text),
+      type: ML_PROP_STRING },
+    { name: "button.reload-opener",
+      offset: ml_offsetof (struct ml_close_button, reload_opener),
+      type: ML_PROP_BOOL },
+    { 0 }
+  };
+
+ml_close_button
+new_ml_close_button (pool pool, const char *text)
+{
+  ml_close_button w = pmalloc (pool, sizeof *w);
+
+  w->ops = &close_button_ops;
+  w->pool = pool;
+  w->text = text;
+  w->reload_opener = 0;
+
+  return w;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_close_button w = (ml_close_button) vw;
+
+  if (w->text)
+    {
+      const char *js;
+
+      if (!w->reload_opener)
+       js = "top.close()";
+      else
+       js = "window.opener.location.reload(); top.close()";
+
+      io_fprintf (io,
+                 "<a class=\"ml_button\" href=\"javascript:%s\">%s</a>",
+                 js, w->text);
+    }
+}
diff --git a/src/ml_close_button.h b/src/ml_close_button.h
new file mode 100644 (file)
index 0000000..70867cb
--- /dev/null
@@ -0,0 +1,56 @@
+/* Monolith close button class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_close_button.h,v 1.3 2002/11/13 21:41:01 rich Exp $
+ */
+
+#ifndef ML_CLOSE_BUTTON_H
+#define ML_CLOSE_BUTTON_H
+
+#include "monolith.h"
+
+struct ml_close_button;
+typedef struct ml_close_button *ml_close_button;
+
+/* Function: new_ml_close_button - monolith button widget
+ *
+ * A close button widget is a simple widget which displays a button.
+ * When pressed, the button closes the current window.
+ *
+ * @code{new_ml_close_button} creates a new close button widget.
+ *
+ * The following properties can be changed on buttons (see
+ * @ref{ml_widget_set_property(3)}):
+ *
+ * @code{text}: The HTML text printed
+ * on the button. Note that the text string must be either
+ * static, or already allocated in the button's pool, or allocated
+ * in a pool with a longer lifetime than the button. If the text
+ * is set to @code{NULL} then this has the same effect as setting
+ * the text to the empty string, which is not very useful because
+ * the button can never be pressed.
+ *
+ * @code{button.reload-opener} (boolean): If set, then when the
+ * close button is pressed, then the window which was responsible
+ * for opening this window (the "opener") is reloaded. This is
+ * useful when we have used a popup button to open a new window,
+ * entered some data into a form in the new window, and then need
+ * to refresh the old window to show the new data.
+ */
+extern ml_close_button new_ml_close_button (pool, const char *text);
+
+#endif /* ML_CLOSE_BUTTON_H */
diff --git a/src/ml_dialog.c b/src/ml_dialog.c
new file mode 100644 (file)
index 0000000..724de62
--- /dev/null
@@ -0,0 +1,221 @@
+/* Monolith dialog class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_dialog.c,v 1.4 2002/11/13 21:41:01 rich Exp $
+ */
+
+#include "config.h"
+
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_table_layout.h"
+#include "ml_flow_layout.h"
+#include "ml_label.h"
+#include "ml_text_label.h"
+#include "ml_image.h"
+#include "ml_button.h"
+#include "ml_close_button.h"
+#include "ml_dialog.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations dialog_ops =
+  {
+    repaint: repaint,
+  };
+
+struct ml_dialog
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+
+  ml_table_layout tbl;         /* Top-level table layout. */
+  ml_text_label title;         /* Title-bar. */
+  ml_image icon;               /* Icon. */
+  ml_label text;               /* Main text. */
+  ml_flow_layout buttons;      /* Buttons. */
+};
+
+ml_dialog
+new_ml_dialog (pool pool)
+{
+  ml_dialog w = pmalloc (pool, sizeof *w);
+
+  w->ops = &dialog_ops;
+  w->pool = pool;
+
+  /* At the top level, a dialog is nothing more than a table layout. */
+  w->tbl = new_ml_table_layout (pool, 3, 2);
+
+  /* Title bar. */
+  w->title = new_ml_text_label (pool, 0);
+  ml_widget_set_property (w->title, "font.weight", "bold");
+  ml_table_layout_pack (w->tbl, w->title, 0, 0);
+  ml_table_layout_set_colspan (w->tbl, 0, 0, 2);
+
+  /* Icon. */
+  w->icon = new_ml_image (pool, 0);
+  ml_table_layout_pack (w->tbl, w->icon, 1, 0);
+
+  /* Text. */
+  w->text = new_ml_label (pool, 0);
+  ml_table_layout_pack (w->tbl, w->text, 1, 1);
+
+  /* Buttons. */
+  w->buttons = new_ml_flow_layout (pool);
+  ml_table_layout_pack (w->tbl, w->buttons, 2, 0);
+  ml_table_layout_set_colspan (w->tbl, 2, 0, 2);
+
+  return w;
+}
+
+void
+ml_dialog_set_text (ml_dialog w, const char *text)
+{
+  ml_widget_set_property (w->text, "text", text);
+}
+
+const char *
+ml_dialog_get_text (ml_dialog w)
+{
+  const char *text;
+
+  ml_widget_get_property (w->text, "text", text);
+  return text;
+}
+
+void
+ml_dialog_set_title (ml_dialog w, const char *title)
+{
+  ml_widget_set_property (w->title, "text", title);
+}
+
+const char *
+ml_dialog_get_title (ml_dialog w)
+{
+  const char *title;
+
+  ml_widget_get_property (w->title, "text", title);
+  return title;
+}
+
+void
+ml_dialog_set_icon (ml_dialog w, const char *icon)
+{
+  ml_widget_set_property (w->icon, "image", icon);
+}
+
+const char *
+ml_dialog_get_icon (ml_dialog w)
+{
+  const char *icon;
+
+  ml_widget_get_property (w->icon, "image", icon);
+  return icon;
+}
+
+void
+ml_dialog_clear_buttons (ml_dialog w)
+{
+  ml_flow_layout_clear (w->buttons);
+}
+
+void
+ml_dialog_add_button (ml_dialog w, const char *text,
+                     void (*callback_fn) (ml_session, void *),
+                     ml_session session, void *data)
+{
+  ml_button b;
+
+  /* Create a new button widget. */
+  b = new_ml_button (w->pool, text);
+  ml_button_set_callback (b, callback_fn, session, data);
+
+  /* Pack it into the buttons flow layout. */
+  ml_flow_layout_pack (w->buttons, b);
+}
+
+void
+ml_dialog_add_close_button (ml_dialog w, const char *text, int flags)
+{
+  ml_close_button b;
+
+  /* Create new button. */
+  b = new_ml_close_button (w->pool, text);
+
+  if ((flags & ML_DIALOG_CLOSE_RELOAD_OPENER))
+    ml_widget_set_property (b, "button.reload-opener", 1);
+
+  /* Pack it. */
+  ml_flow_layout_pack (w->buttons, b);
+}
+
+ml_window
+ml_ok_window (pool pool, ml_session session, const char *text, int flags)
+{
+  ml_window win;
+  ml_dialog dlg;
+  const char *title = "That operation was carried out successfully";
+
+  win = new_ml_window (session, pool);
+  dlg = new_ml_dialog (pool);
+
+  ml_window_set_title (win, title);
+  ml_dialog_set_text (dlg, text);
+  ml_dialog_set_title (dlg, title);
+  // ml_dialog_set_icon (dlg, ...);
+
+  if ((flags & ML_DIALOG_CLOSE_BUTTON))
+    ml_dialog_add_close_button (dlg, "Close window", flags);
+
+  ml_window_pack (win, dlg);
+
+  return win;
+}
+
+ml_window
+ml_error_window (pool pool, ml_session session, const char *text, int flags)
+{
+  ml_window win;
+  ml_dialog dlg;
+  const char *title = "There was an error";
+
+  win = new_ml_window (session, pool);
+  dlg = new_ml_dialog (pool);
+
+  ml_window_set_title (win, title);
+  ml_dialog_set_text (dlg, text);
+  ml_dialog_set_title (dlg, title);
+  // ml_dialog_set_icon (dlg, ...);
+
+  if ((flags & ML_DIALOG_CLOSE_BUTTON))
+    ml_dialog_add_close_button (dlg, "Close window", flags);
+
+  ml_window_pack (win, dlg);
+
+  return win;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_dialog w = (ml_dialog) vw;
+
+  ml_widget_repaint (w->tbl, session, windowid, io);
+}
diff --git a/src/ml_dialog.h b/src/ml_dialog.h
new file mode 100644 (file)
index 0000000..742a6d5
--- /dev/null
@@ -0,0 +1,134 @@
+/* Monolith dialog class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_dialog.h,v 1.3 2002/11/13 21:41:01 rich Exp $
+ */
+
+#ifndef ML_DIALOG_H
+#define ML_DIALOG_H
+
+#include "monolith.h"
+#include "ml_window.h"
+
+struct ml_dialog;
+typedef struct ml_dialog *ml_dialog;
+
+/* Function: new_ml_dialog - monolith dialog widget
+ * Function: ml_dialog_set_text
+ * Function: ml_dialog_get_text
+ * Function: ml_dialog_set_title
+ * Function: ml_dialog_get_title
+ * Function: ml_dialog_set_icon
+ * Function: ml_dialog_get_icon
+ * Function: ml_dialog_clear_buttons
+ * Function: ml_dialog_add_button
+ * Function: ml_dialog_add_close_button
+ *
+ * A dialog is a widget for asking a user a question, and getting an
+ * answer. It can also be used for presenting the user with
+ * confirmation that some operation has been carried out, or for
+ * presenting the user with an error message.
+ *
+ * Fundamentally, a dialog consists of some text, and a series of
+ * zero or more buttons along the bottom of the widget. A dialog
+ * can also have an optional title which appears along the top
+ * and an optional icon which appears on the left of the window.
+ *
+ * Dialogs are almost always used packed directly into a window.
+ *
+ * Dialogs are actually "super-widgets", built up from fundamental
+ * widgets like labels, buttons, and table layouts. However currently
+ * none of the fundamental widgets are actually exposed through the
+ * @code{ml_dialog_*} interface.
+ *
+ * @code{new_ml_dialog} creates a new dialog widget with no text, no
+ * title, no icon and no buttons (ie. not very useful! - after calling
+ * this you should immediately call @code{ml_dialog_set_text} and
+ * probably @code{ml_dialog_add_button} too).
+ *
+ * @code{ml_dialog_(set|get)_text} updates the text in a dialog
+ * widget. Although having text is not strictly mandatory, it is
+ * highly advisable.
+ *
+ * @code{ml_dialog_(set|get)_title} changes the title. To have no
+ * title, set the title to @code{NULL}.
+ *
+ * @code{ml_dialog_(set|get)_icon} changes the icon. To have no
+ * icon, set the icon to @code{NULL}.
+ *
+ * @code{ml_dialog_clear_buttons} removes all the buttons from the
+ * dialog.
+ *
+ * @code{ml_dialog_add_button} adds a single button to the dialog. If
+ * there are already buttons attached to the dialog, then this adds
+ * the new button on the right. The @code{text} which appears on
+ * the button must be specified. When the button is pressed,
+ * @code{callback_fn} will be called.
+ *
+ * @code{ml_dialog_add_close_button} adds a single close button to the
+ * dialog. The @code{text} which appears on the button must be specified.
+ * If @code{close_flags} contains @code{ML_DIALOG_CLOSE_RELOAD_OPENER}
+ * then the close button will cause the opener window to reload (see
+ * @ref{new_ml_close_button(3)}).
+ *
+ * See also: @ref{ml_ok_window(3)}, @ref{ml_error_window(3)}.
+ */
+extern ml_dialog new_ml_dialog (pool pool);
+extern void ml_dialog_set_text (ml_dialog, const char *text);
+extern const char *ml_dialog_get_text (ml_dialog);
+extern void ml_dialog_set_title (ml_dialog, const char *title);
+extern const char *ml_dialog_get_title (ml_dialog);
+extern void ml_dialog_set_icon (ml_dialog, const char *icon);
+extern const char *ml_dialog_get_icon (ml_dialog);
+extern void ml_dialog_clear_buttons (ml_dialog);
+extern void ml_dialog_add_button (ml_dialog, const char *text, void (*callback_fn) (ml_session, void *), ml_session session, void *data);
+extern void ml_dialog_add_close_button (ml_dialog w, const char *text, int close_flags);
+
+/* Function: ml_ok_window - confirmation and error windows
+ * Function: ml_error_window
+ *
+ * @code{ml_ok_window} and @code{ml_error_window} are handy helper
+ * functions which display either a confirmation of success, or error
+ * window. They are just convenient wrappers around @code{new_ml_window}
+ * and @code{new_ml_dialog}.
+ *
+ * @code{ml_ok_window} is a window which displays a confirmation of
+ * success. The @code{session} argument is the current session. The
+ * @code{text} is the text to display. @code{flags} is explained below.
+ *
+ * @code{ml_error_window} is a window which displays an error
+ * message. The @code{session} argument is the current session. The
+ * @code{text} is the text to display. @code{flags} is explained below.
+ *
+ * The @code{flags} argument is a list of the following flags:
+ *
+ * @code{ML_DIALOG_CLOSE_BUTTON}: If set, display a close window button.
+ *
+ * @code{ML_DIALOG_CLOSE_RELOAD_OPENER}: If set, refresh the opener
+ * window on close (see @ref{new_ml_close_button(3)}).
+ *
+ * Both functions return the new window.
+ *
+ * See also: @ref{new_ml_window(3)}, @ref{new_ml_dialog(3)}.
+ */
+extern ml_window ml_ok_window (pool, ml_session, const char *text, int flags);
+extern ml_window ml_error_window (pool, ml_session, const char *text, int flags);
+
+#define ML_DIALOG_CLOSE_BUTTON        0x0001
+#define ML_DIALOG_CLOSE_RELOAD_OPENER 0x0002
+
+#endif /* ML_DIALOG_H */
diff --git a/src/ml_flow_layout.c b/src/ml_flow_layout.c
new file mode 100644 (file)
index 0000000..237ff7c
--- /dev/null
@@ -0,0 +1,143 @@
+/* Monolith flow layout class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_flow_layout.c,v 1.1 2002/08/28 19:50:28 rich Exp $
+ */
+
+#include "config.h"
+
+#include <pthr_iolib.h>
+
+#include "ml_widget.h"
+#include "monolith.h"
+#include "ml_flow_layout.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations flow_layout_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_flow_layout
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  vector v;                    /* Vector of widgets. */
+};
+
+ml_flow_layout
+new_ml_flow_layout (pool pool)
+{
+  ml_flow_layout w = pmalloc (pool, sizeof *w);
+
+  w->ops = &flow_layout_ops;
+  w->pool = pool;
+  w->v = new_vector (pool, ml_widget);
+
+  return w;
+}
+
+void
+ml_flow_layout_push_back (ml_flow_layout w, ml_widget _w)
+{
+  vector_push_back (w->v, _w);
+}
+
+void
+ml_flow_layout_pack (ml_flow_layout w, ml_widget _w)
+{
+  vector_push_back (w->v, _w);
+}
+
+ml_widget
+ml_flow_layout_pop_back (ml_flow_layout w)
+{
+  ml_widget _w;
+
+  vector_pop_back (w->v, _w);
+  return _w;
+}
+
+void
+ml_flow_layout_push_front (ml_flow_layout w, ml_widget _w)
+{
+  vector_push_front (w->v, _w);
+}
+
+ml_widget
+ml_flow_layout_pop_front (ml_flow_layout w)
+{
+  ml_widget _w;
+
+  vector_pop_front (w->v, _w);
+  return _w;
+}
+
+ml_widget
+ml_flow_layout_get (ml_flow_layout w, int i)
+{
+  ml_widget _w;
+
+  vector_get (w->v, i, _w);
+  return _w;
+}
+
+void
+ml_flow_layout_insert (ml_flow_layout w, int i, ml_widget _w)
+{
+  vector_insert (w->v, i, _w);
+}
+
+void
+ml_flow_layout_replace (ml_flow_layout w, int i, ml_widget _w)
+{
+  vector_replace (w->v, i, _w);
+}
+
+void
+ml_flow_layout_erase (ml_flow_layout w, int i)
+{
+  vector_erase (w->v, i);
+}
+
+void
+ml_flow_layout_clear (ml_flow_layout w)
+{
+  vector_clear (w->v);
+}
+
+int
+ml_flow_layout_size (ml_flow_layout w)
+{
+  return vector_size (w->v);
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_flow_layout w = (ml_flow_layout) vw;
+  int i;
+
+  for (i = 0; i < vector_size (w->v); ++i)
+    {
+      ml_widget _w;
+
+      vector_get (w->v, i, _w);
+      ml_widget_repaint (_w, session, windowid, io);
+    }
+}
diff --git a/src/ml_flow_layout.h b/src/ml_flow_layout.h
new file mode 100644 (file)
index 0000000..a471b4a
--- /dev/null
@@ -0,0 +1,74 @@
+/* Monolith flow layout class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_flow_layout.h,v 1.2 2002/08/30 14:28:46 rich Exp $
+ */
+
+#ifndef ML_FLOW_LAYOUT_H
+#define ML_FLOW_LAYOUT_H
+
+#include <ml_widget.h>
+
+struct ml_flow_layout;
+typedef struct ml_flow_layout *ml_flow_layout;
+
+/* Function: new_ml_flow_layout - monolith flow_layout widget
+ * Function: ml_flow_layout_push_back
+ * Function: ml_flow_layout_pop_back
+ * Function: ml_flow_layout_push_front
+ * Function: ml_flow_layout_pop_front
+ * Function: ml_flow_layout_get
+ * Function: ml_flow_layout_insert
+ * Function: ml_flow_layout_replace
+ * Function: ml_flow_layout_erase
+ * Function: ml_flow_layout_clear
+ * Function: ml_flow_layout_size
+ * Function: ml_flow_layout_pack
+ *
+ * A flow layout widget is the simplest type of layout widget. It
+ * contains an ordered list of widgets, and it simply arranges them
+ * one after another (in other words with no "layout" at all).
+ *
+ * @code{new_ml_flow_layout} creates a new flow layout widget.
+ *
+ * Underlying the flow layout widget is a simple c2lib vector, and the
+ * other access functions use the same notation as the equivalent
+ * c2lib @code{vector_*} functions. Go to the SEE ALSO section
+ * below to see how to manipulate widgets within a flow layout.
+ *
+ * @code{ml_flow_layout_pack} is equivalent to @code{ml_flow_layout_push_back}:
+ * it appends the widget to the end of the current vector of widgets.
+ *
+ * See also: @ref{vector_push_back(3)}, @ref{vector_pop_back(3)},
+ * @ref{vector_push_front(3)}, @ref{vector_pop_front(3)},
+ * @ref{vector_get(3)}, @ref{vector_insert(3)}, @ref{vector_replace(3)},
+ * @ref{vector_erase(3)}, @ref{vector_clear(3)}, @ref{vector_size(3)}.
+ */
+extern ml_flow_layout new_ml_flow_layout (pool pool);
+extern void ml_flow_layout_push_back (ml_flow_layout, ml_widget);
+extern ml_widget ml_flow_layout_pop_back (ml_flow_layout);
+extern void ml_flow_layout_push_front (ml_flow_layout, ml_widget);
+extern ml_widget ml_flow_layout_pop_front (ml_flow_layout);
+extern ml_widget ml_flow_layout_get (ml_flow_layout, int i);
+extern void ml_flow_layout_insert (ml_flow_layout, int i, ml_widget);
+extern void ml_flow_layout_replace (ml_flow_layout, int i, ml_widget);
+extern void ml_flow_layout_erase (ml_flow_layout, int i);
+extern void ml_flow_layout_clear (ml_flow_layout);
+extern int ml_flow_layout_size (ml_flow_layout);
+extern void ml_flow_layout_pack (ml_flow_layout, ml_widget);
+
+#endif /* ML_FLOW_LAYOUT_H */
diff --git a/src/ml_form.c b/src/ml_form.c
new file mode 100644 (file)
index 0000000..6179410
--- /dev/null
@@ -0,0 +1,207 @@
+/* Monolith form class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form.c,v 1.6 2002/11/23 16:46:05 rich Exp $
+ */
+
+#include "config.h"
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#include <vector.h>
+#include <hash.h>
+#include <pstring.h>
+
+#include <pthr_iolib.h>
+#include <pthr_cgi.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_form_input.h"
+#include "ml_form.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property properties[];
+
+struct ml_widget_operations form_ops =
+  {
+    repaint: repaint,
+    properties: properties,
+  };
+
+struct ml_form
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_widget w;                 /* Packed widget. */
+  const char *action_id;       /* Form's callback action. */
+  const char *method;          /* Either "GET" or "POST". */
+  const char *name;            /* Unique name of the form. */
+
+  /* This is the user's real callback function. */
+  void (*submit_fn) (ml_session, void *);
+  void *submit_data;
+
+  vector inputs;               /* List of input widgets. */
+};
+
+static struct ml_widget_property properties[] =
+  {
+    { name: "method",
+      offset: ml_offsetof (struct ml_form, method),
+      type: ML_PROP_STRING },
+    { name: "form.name",
+      offset: ml_offsetof (struct ml_form, name),
+      type: ML_PROP_STRING,
+      flags: ML_PROP_READ_ONLY },
+    { 0 },
+  };
+
+struct form_input
+{
+  ml_form_input w;             /* Widget pointer. */
+  const char *name;            /* Name of input widget. */
+};
+
+static void form_callback (ml_session, void *);
+
+ml_form
+new_ml_form (pool pool)
+{
+  static int unique = 0;
+  ml_form w = pmalloc (pool, sizeof *w);
+
+  w->ops = &form_ops;
+  w->pool = pool;
+  w->w = 0;
+  w->action_id = 0;
+  w->method = "POST";
+  w->name = psprintf (pool, "ml_form%d", ++unique);
+  w->submit_fn = 0;
+  w->submit_data = 0;
+  w->inputs = new_vector (pool, struct form_input);
+
+  return w;
+}
+
+void
+ml_form_set_callback (ml_form w, void (*callback_fn) (ml_session, void *),
+                     ml_session session, void *data)
+{
+  if (w->action_id)
+    ml_unregister_action (session, w->action_id);
+  w->action_id = 0;
+  w->submit_fn = 0;
+  w->submit_data = 0;
+
+  if (callback_fn)
+    {
+      w->action_id = ml_register_action (session, form_callback, w);
+      w->submit_fn = callback_fn;
+      w->submit_data = data;
+    }
+}
+
+void
+ml_form_pack (ml_form w, ml_widget _w)
+{
+  w->w = _w;
+}
+
+/* We capture the callback function when the submit button is pressed
+ * and allow ourselves to update the values fields of all our input
+ * widgets first.
+ */
+static void
+form_callback (ml_session session, void *vw)
+{
+  pool session_pool = ml_session_pool (session);
+  ml_form w = (ml_form) vw;
+  cgi args;
+  int i;
+
+  /* Get the arguments passed to this HTTP request. */
+  args = _ml_session_submitted_args (session);
+
+  /* Update the values of submitted form elements. */
+  for (i = 0; i < vector_size (w->inputs); ++i)
+    {
+      struct form_input input;
+      vector values;
+      const char *value;
+      int j;
+
+      vector_get (w->inputs, i, input);
+      values = cgi_param_list (args, input.name);
+
+      /* Clear value first. */
+      ml_form_input_clear_value (input.w);
+
+      /* Set value(s). */
+      if (values)
+       for (j = 0; j < vector_size (values); ++j)
+         {
+           vector_get (values, j, value);
+           ml_form_input_set_value (input.w, pstrdup (session_pool, value));
+         }
+    }
+
+  /* Call the user's real callback function. */
+  assert (w->submit_fn != 0);
+  w->submit_fn (session, w->submit_data);
+}
+
+const char *
+_ml_form_register_widget (ml_form f, ml_form_input w)
+{
+  struct form_input input;
+  static int name_id = 0;
+
+  input.name = psprintf (f->pool, "ml_f%d", ++name_id);
+  input.w = w;
+
+  vector_push_back (f->inputs, input);
+
+  return input.name;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_form w = (ml_form) vw;
+
+  if (w->w)
+    {
+      if (w->action_id)
+       io_fprintf (io,
+                   "<form method=\"%s\" action=\"%s\" name=\"%s\">"
+                   "<input type=\"hidden\" name=\"ml_window\" value=\"%s\" />"
+                   "<input type=\"hidden\" name=\"ml_action\" value=\"%s\" />"
+                   ,
+                   w->method,
+                   ml_session_script_name (session),
+                   w->name,
+                   windowid,
+                   w->action_id);
+      else
+       io_fputs ("<form>", io);
+      ml_widget_repaint (w->w, session, windowid, io);
+      io_fputs ("</form>", io);
+    }
+}
diff --git a/src/ml_form.h b/src/ml_form.h
new file mode 100644 (file)
index 0000000..9c85e3e
--- /dev/null
@@ -0,0 +1,95 @@
+/* Monolith form class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form.h,v 1.5 2002/11/23 16:46:05 rich Exp $
+ */
+
+#ifndef ML_FORM_H
+#define ML_FORM_H
+
+#include "monolith.h"
+#include "ml_form_input.h"
+
+struct ml_form;
+typedef struct ml_form *ml_form;
+
+/* Function: new_ml_form - monolith form widget
+ * Function: ml_form_set_callback
+ * Function: ml_form_pack
+ *
+ * A monolith form widget is a low-level widget for handling input of
+ * text fields, check buttons, radio buttons and so on, collected together
+ * into a single form on a page.
+ *
+ * A form should contain a mixture of input widgets and ordinary widgets.
+ * Since the form widget itself can only be packed with a single widget,
+ * normally you would pack a layout widget directly into the form, and
+ * then a mixture of labels and input widgets into the layout widget.
+ *
+ * One or more of the widgets packed into the form can be a submit
+ * button (see @ref{new_ml_form_submit(3)}). Pressing on the submit
+ * button causes the form's callback function to be called, and in this
+ * function the values entered into the other input widgets can be
+ * read. It is recommended that all forms contain at least one submit
+ * button, because the effect of creating a form with no submit buttons
+ * is browser-dependent, and can mean that the form cannot be submitted.
+ *
+ * Forms cannot be nested (a form widget cannot contain another form
+ * widget inside itself). This is a limitation of HTML.
+ *
+ * Forms are low-level entities. To do seriously interesting things with
+ * forms such as self-validation, use one of the higher-level form-type
+ * abstractions that monolith provides (XXX will provide, not now - RWMJ).
+ *
+ * @code{new_ml_form} creates a new form widget.
+ *
+ * @code{ml_form_set_callback} sets the callback function which is
+ * called when the form is submitted. The callback function is invoked
+ * as @code{void callback_fn (ml_session session, void *data)}. The
+ * values that the user entered in the input fields are available
+ * by calling (for example) @ref{ml_form_input_get_value(3)} on each
+ * input widget within the form.
+ *
+ * @code{ml_form_pack} packs a widget into the form. A form can only
+ * store a single widget, so if you call pack subsequent times, the
+ * earlier widgets are forgotten.
+ *
+ * The following properties can be changed on forms (see
+ * @ref{ml_widget_set_property(3)}):
+ *
+ * @code{method}: This is an esoteric property that you will
+ * probably never need to use. It changes the behaviour of the
+ * HTML form to use either @code{"GET"} or @code{"POST"} - the
+ * default being @code{"POST"}.
+ *
+ * @code{form.name}: A read-only property containing the name of
+ * the form. It is unlikely that you will ever need to know this.
+ *
+ * See also: @ref{new_ml_form_input(3)}, @ref{new_ml_form_textarea(3)},
+ * @ref{new_ml_form_submit(3)}.
+ */
+extern ml_form new_ml_form (pool pool);
+extern void ml_form_set_callback (ml_form, void (*callback_fn) (ml_session, void *), ml_session session, void *data);
+extern void ml_form_pack (ml_form, ml_widget);
+
+/* Form input widgets must call this function during their 'new' phase
+ * to register themselves with the form. The function returns a unique
+ * name for the form input field.
+ */
+extern const char *_ml_form_register_widget (ml_form f, ml_form_input w);
+
+#endif /* ML_FORM_H */
diff --git a/src/ml_form_checkbox.c b/src/ml_form_checkbox.c
new file mode 100644 (file)
index 0000000..03ff88e
--- /dev/null
@@ -0,0 +1,110 @@
+/* Monolith form checkbox input class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_checkbox.c,v 1.1 2002/08/30 14:28:47 rich Exp $
+ */
+
+#include "config.h"
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_form_input.h"
+#include "ml_form_checkbox.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static void clear_value (void *);
+static void set_value (void *, const char *value);
+static const char *get_value (void *);
+
+struct ml_widget_operations form_checkbox_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_form_input_operations form_checkbox_input_ops =
+  {
+    clear_value: clear_value,
+    set_value: set_value,
+    get_value: get_value
+  };
+
+struct ml_form_checkbox
+{
+  struct ml_widget_operations *ops;
+  struct ml_form_input_operations *fops;
+  pool pool;                   /* Pool for allocations. */
+  const char *name;            /* Name of the input field. */
+  const char *value;           /* Value of the input field. */
+};
+
+ml_form_checkbox
+new_ml_form_checkbox (pool pool, ml_form form)
+{
+  ml_form_checkbox w = pmalloc (pool, sizeof *w);
+
+  w->ops = &form_checkbox_ops;
+  w->fops = &form_checkbox_input_ops;
+  w->pool = pool;
+  w->value = 0;
+
+  /* Register ourselves with the form. */
+  w->name = _ml_form_register_widget (form, w);
+
+  return w;
+}
+
+static void
+clear_value (void *vw)
+{
+  ml_form_checkbox w = (ml_form_checkbox) vw;
+
+  w->value = 0;
+}
+
+static void
+set_value (void *vw, const char *value)
+{
+  ml_form_checkbox w = (ml_form_checkbox) vw;
+
+  assert (value);
+  w->value = "1";
+}
+
+static const char *
+get_value (void *vw)
+{
+  ml_form_checkbox w = (ml_form_checkbox) vw;
+
+  return w->value;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_form_checkbox w = (ml_form_checkbox) vw;
+
+  io_fprintf (io, "<input class=\"ml_form_checkbox\" type=\"checkbox\" "
+             "name=\"%s\" value=\"1\""
+             "%s />",
+             w->name, w->value ? " checked=\"1\"" : "");
+}
diff --git a/src/ml_form_checkbox.h b/src/ml_form_checkbox.h
new file mode 100644 (file)
index 0000000..7a51cb0
--- /dev/null
@@ -0,0 +1,42 @@
+/* Monolith for checkbox input class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_checkbox.h,v 1.1 2002/08/30 14:28:47 rich Exp $
+ */
+
+#ifndef ML_FORM_CHECKBOX_H
+#define ML_FORM_CHECKBOX_H
+
+#include "monolith.h"
+#include "ml_form.h"
+
+struct ml_form_checkbox;
+typedef struct ml_form_checkbox *ml_form_checkbox;
+
+/* Function: new_ml_form_checkbox - monolith form checkbox input widget
+ *
+ * This is a checkbox (tickbox) for use in forms.
+ *
+ * @code{new_ml_form_checkbox} creates a new form checkbox input widget. The
+ * form into which this widget is being embedded is passed as the
+ * @code{form} parameter.
+ *
+ * See also: @ref{new_ml_form(3)}, @ref{ml_form_input_get_value(3)}.
+ */
+extern ml_form_checkbox new_ml_form_checkbox (pool pool, ml_form form);
+
+#endif /* ML_FORM_CHECKBOX_H */
diff --git a/src/ml_form_input.c b/src/ml_form_input.c
new file mode 100644 (file)
index 0000000..bb35fda
--- /dev/null
@@ -0,0 +1,56 @@
+/* Monolith form input class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_input.c,v 1.3 2002/08/30 14:28:47 rich Exp $
+ */
+
+#include "config.h"
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_form_input.h"
+
+/* This is what generic form input objects *actually* look like inside. */
+struct form_input
+{
+  struct ml_widget_operations *ops;
+  struct ml_form_input_operations *fops;
+};
+
+void
+ml_form_input_set_value (void *vw, const char *value)
+{
+  struct form_input *w = (struct form_input *) vw;
+
+  w->fops->set_value (vw, value);
+}
+
+const char *
+ml_form_input_get_value (void *vw)
+{
+  struct form_input *w = (struct form_input *) vw;
+
+  return w->fops->get_value (vw);
+}
+
+void
+ml_form_input_clear_value (void *vw)
+{
+  struct form_input *w = (struct form_input *) vw;
+
+  w->fops->clear_value (vw);
+}
diff --git a/src/ml_form_input.h b/src/ml_form_input.h
new file mode 100644 (file)
index 0000000..af570ae
--- /dev/null
@@ -0,0 +1,55 @@
+/* Monolith form input class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_input.h,v 1.3 2002/08/30 14:28:47 rich Exp $
+ */
+
+#ifndef ML_FORM_INPUT_H
+#define ML_FORM_INPUT_H
+
+#include "monolith.h"
+
+/* As with widgets, form inputs are opaque except within ml_form_input.c
+ * itself.
+ */
+typedef void *ml_form_input;
+
+/* A pointer to this struct must occupy the second slot in every
+ * form input structure we define elsewhere (just after the widget
+ * pointer in the first slot).
+ */
+struct ml_form_input_operations
+{
+  void (*clear_value) (void *form_input);
+  void (*set_value) (void *form_input, const char *value);
+  const char *(*get_value) (void *form_input);
+};
+
+/* Function: ml_form_input_set_value - Operations on generic form inputs.
+ * Function: ml_form_input_get_value
+ * Function: ml_form_input_clear_value
+ *
+ * @code{ml_form_input_(set|get)_value} update the value field in any
+ * form input.
+ *
+ * @code{ml_form_input_clear_value} clears (nulls) the value field.
+ */
+extern void ml_form_input_set_value (ml_form_input form_input, const char *value);
+extern const char *ml_form_input_get_value (ml_form_input form_input);
+extern void ml_form_input_clear_value (ml_form_input form_input);
+
+#endif /* ML_FORM_INPUT_H */
diff --git a/src/ml_form_layout.c b/src/ml_form_layout.c
new file mode 100644 (file)
index 0000000..5671ed2
--- /dev/null
@@ -0,0 +1,121 @@
+/* Monolith form layout class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_layout.c,v 1.1 2002/11/13 20:46:37 rich Exp $
+ */
+
+#include "config.h"
+
+#include <pthr_iolib.h>
+
+#include "ml_widget.h"
+#include "monolith.h"
+#include "ml_text_label.h"
+#include "ml_multicol_layout.h"
+#include "ml_form_layout.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property properties[];
+
+struct ml_widget_operations form_layout_ops =
+  {
+    repaint: repaint,
+    properties: properties,
+  };
+
+struct ml_form_layout
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_multicol_layout tbl;      /* We're really a multi-column layout. */
+  const char *clazz;           /* Stylesheet class. */
+};
+
+static void update_table_class (void *vw);
+
+static struct ml_widget_property properties[] =
+  {
+    { name: "class",
+      offset: ml_offsetof (struct ml_form_layout, clazz),
+      type: ML_PROP_STRING,
+      on_set: update_table_class },
+    { 0 }
+  };
+
+ml_form_layout
+new_ml_form_layout (pool pool)
+{
+  ml_form_layout w = pmalloc (pool, sizeof *w);
+
+  w->ops = &form_layout_ops;
+  w->pool = pool;
+  w->tbl = new_ml_multicol_layout (pool, 2);
+  w->clazz = "ml_form_layout";
+  update_table_class (w);
+
+  return w;
+}
+
+static void
+update_table_class (void *vw)
+{
+  ml_form_layout w = (ml_form_layout) vw;
+
+  ml_widget_set_property (w->tbl, "class", w->clazz);
+}
+
+void
+ml_form_layout_pack (ml_form_layout w, const char *label, ml_widget input)
+{
+  ml_text_label lbl;
+
+  if (label)
+    {
+      lbl = new_ml_text_label (w->pool, label);
+
+      ml_multicol_layout_set_header (w->tbl, 1);
+      ml_multicol_layout_pack (w->tbl, lbl);
+    }
+  else
+    {
+      ml_multicol_layout_set_header (w->tbl, 1);
+      ml_multicol_layout_pack (w->tbl, 0);
+    }
+
+  ml_multicol_layout_pack (w->tbl, input);
+}
+
+void
+ml_form_layout_pack_help (ml_form_layout w, const char *help_text)
+{
+  ml_text_label lbl;
+
+  ml_multicol_layout_set_header (w->tbl, 1);
+  ml_multicol_layout_pack (w->tbl, 0);
+
+  lbl = new_ml_text_label (w->pool, help_text);
+  ml_widget_set_property (lbl, "font.size", "small");
+  ml_multicol_layout_pack (w->tbl, lbl);
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_form_layout w = (ml_form_layout) vw;
+
+  ml_widget_repaint (w->tbl, session, windowid, io);
+}
diff --git a/src/ml_form_layout.h b/src/ml_form_layout.h
new file mode 100644 (file)
index 0000000..090fb95
--- /dev/null
@@ -0,0 +1,63 @@
+/* Monolith form layout class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_layout.h,v 1.1 2002/11/13 20:46:37 rich Exp $
+ */
+
+#ifndef ML_FORM_LAYOUT_H
+#define ML_FORM_LAYOUT_H
+
+#include <ml_widget.h>
+
+struct ml_form_layout;
+typedef struct ml_form_layout *ml_form_layout;
+
+/* Function: new_ml_form_layout - monolith form layout widget
+ * Function: ml_form_layout_pack
+ * Function: ml_form_layout_pack_help
+ *
+ * This is the monolith form layout widget. It is used to provide a
+ * very easy way to lay out forms. It contains two columns: the left
+ * hand column contains the field names, and the right hand column
+ * contains the form input widgets.
+ *
+ * It is a specialised version of @code{ml_multicol_layout}, which is
+ * itself a specialised version of @code{ml_table_layout}.
+ *
+ * @code{new_ml_form_layout} creates a new form layout widget.
+ *
+ * The following properties can be changed on form layouts (see
+ * @ref{ml_widget_set_property(3)}):
+ *
+ * @code{class}: The stylesheet class.
+ *
+ * @code{ml_form_layout_pack} packs a row in the form layout widget.
+ * The @code{label} string is the label for the row (normally
+ * something like @code{"field name:"}. This is placed in the left
+ * hand column, and rendered in bold, right-aligned. @code{label} may also be
+ * @code{NULL} for no label. @code{input} is a widget (normally
+ * a form input widget), which is placed in the right hand column.
+ *
+ * @code{ml_form_layout_pack_help} packs a row which consists of
+ * help information. This is placed in the right hand column and
+ * rendered as small text.
+ */
+extern ml_form_layout new_ml_form_layout (pool pool);
+extern void ml_form_layout_pack (ml_form_layout, const char *label, ml_widget input);
+extern void ml_form_layout_pack_help (ml_form_layout, const char *help_text);
+
+#endif /* ML_FORM_LAYOUT_H */
diff --git a/src/ml_form_password.c b/src/ml_form_password.c
new file mode 100644 (file)
index 0000000..5175308
--- /dev/null
@@ -0,0 +1,112 @@
+/* Monolith form password input class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_password.c,v 1.2 2003/01/11 16:39:09 rich Exp $
+ */
+
+#include "config.h"
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_smarttext.h"
+#include "ml_form_input.h"
+#include "ml_form_password.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static void clear_value (void *);
+static void set_value (void *, const char *value);
+static const char *get_value (void *);
+
+struct ml_widget_operations form_password_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_form_input_operations form_password_input_ops =
+  {
+    clear_value: clear_value,
+    set_value: set_value,
+    get_value: get_value
+  };
+
+struct ml_form_password
+{
+  struct ml_widget_operations *ops;
+  struct ml_form_input_operations *fops;
+  pool pool;                   /* Pool for allocations. */
+  const char *name;            /* Name of the input field. */
+  const char *value;           /* Value of the input field. */
+};
+
+ml_form_password
+new_ml_form_password (pool pool, ml_form form)
+{
+  ml_form_password w = pmalloc (pool, sizeof *w);
+
+  w->ops = &form_password_ops;
+  w->fops = &form_password_input_ops;
+  w->pool = pool;
+  w->value = 0;
+
+  /* Register ourselves with the form. */
+  w->name = _ml_form_register_widget (form, w);
+
+  return w;
+}
+
+static void
+clear_value (void *vw)
+{
+  ml_form_password w = (ml_form_password) vw;
+
+  w->value = 0;
+}
+
+static void
+set_value (void *vw, const char *value)
+{
+  ml_form_password w = (ml_form_password) vw;
+
+  assert (value);
+  w->value = value;
+}
+
+static const char *
+get_value (void *vw)
+{
+  ml_form_password w = (ml_form_password) vw;
+
+  return w->value;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_form_password w = (ml_form_password) vw;
+
+  io_fprintf (io, "<input class=\"ml_form_password\" type=\"password\" "
+             "name=\"%s\" value=\"",
+             w->name);
+  if (w->value) ml_plaintext_print (io, w->value);
+  io_fputs ("\" />", io);
+}
diff --git a/src/ml_form_password.h b/src/ml_form_password.h
new file mode 100644 (file)
index 0000000..3710f05
--- /dev/null
@@ -0,0 +1,44 @@
+/* Monolith form password input class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_password.h,v 1.1 2002/08/30 14:28:47 rich Exp $
+ */
+
+#ifndef ML_FORM_PASSWORD_H
+#define ML_FORM_PASSWORD_H
+
+#include "monolith.h"
+#include "ml_form.h"
+
+struct ml_form_password;
+typedef struct ml_form_password *ml_form_password;
+
+/* Function: new_ml_form_password - monolith form password input widget
+ *
+ * This is a single line text input field, for use in forms. The
+ * field is rendered by the browser obscured (often with "*" for
+ * characters). However it is passed over the network in plain text.
+ *
+ * @code{new_ml_form_password} creates a new form password input widget. The
+ * form into which this widget is being embedded is passed as the
+ * @code{form} parameter.
+ *
+ * See also: @ref{new_ml_form(3)}, @ref{ml_form_input_get_value(3)}.
+ */
+extern ml_form_password new_ml_form_password (pool pool, ml_form form);
+
+#endif /* ML_FORM_PASSWORD_H */
diff --git a/src/ml_form_radio.c b/src/ml_form_radio.c
new file mode 100644 (file)
index 0000000..4616ae5
--- /dev/null
@@ -0,0 +1,108 @@
+/* Monolith form radio button class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_radio.c,v 1.1 2002/08/30 14:28:47 rich Exp $
+ */
+
+#include "config.h"
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_form_input.h"
+#include "ml_form_radio.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static const char *get_value (void *);
+
+struct ml_widget_operations form_radio_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_form_input_operations form_radio_input_ops =
+  {
+    clear_value: 0,
+    set_value: 0,
+    get_value: get_value
+  };
+
+struct ml_form_radio
+{
+  struct ml_widget_operations *ops;
+  struct ml_form_input_operations *fops;
+  pool pool;                   /* Pool for allocations. */
+  const char *name;            /* Name of the input field. */
+  const char *value;           /* Value of the input field. */
+  int is_checked;              /* Radio button checked? */
+};
+
+ml_form_radio
+new_ml_form_radio (pool pool, ml_form_radio_group group, const char *value)
+{
+  ml_form_radio w = pmalloc (pool, sizeof *w);
+
+  assert (value);
+
+  w->ops = &form_radio_ops;
+  w->fops = &form_radio_input_ops;
+  w->pool = pool;
+  w->value = value;
+  w->is_checked = 0;
+
+  /* Register ourselves with the radio button group widget. */
+  w->name = _ml_form_radio_group_register (group, w, value);
+
+  return w;
+}
+
+void
+ml_form_radio_set_checked (ml_form_radio w, int checked)
+{
+  w->is_checked = checked;
+}
+
+int
+ml_form_radio_get_checked (ml_form_radio w)
+{
+  return w->is_checked;
+}
+
+static const char *
+get_value (void *vw)
+{
+  ml_form_radio w = (ml_form_radio) vw;
+
+  return w->value;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_form_radio w = (ml_form_radio) vw;
+
+  io_fprintf (io, "<input class=\"ml_form_radio\" type=\"radio\" "
+             "name=\"%s\" value=\"%s\""
+             "%s />",
+             w->name, w->value,
+             w->is_checked ? " checked=\"1\"" : "");
+}
diff --git a/src/ml_form_radio.h b/src/ml_form_radio.h
new file mode 100644 (file)
index 0000000..1ac64c3
--- /dev/null
@@ -0,0 +1,54 @@
+/* Monolith form radio button input class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_radio.h,v 1.1 2002/08/30 14:28:47 rich Exp $
+ */
+
+#ifndef ML_FORM_RADIO_H
+#define ML_FORM_RADIO_H
+
+#include "monolith.h"
+#include "ml_form_radio_group.h"
+
+struct ml_form_radio;
+typedef struct ml_form_radio *ml_form_radio;
+
+/* Function: new_ml_form_radio - monolith radio button widget
+ * Function: ml_form_radio_set_checked
+ * Function: ml_form_radio_get_checked
+ *
+ * This is a radio button input, for use in forms. Radio buttons
+ * represent choices, and thus are grouped together. For this reason,
+ * you must place all related radio buttons inside a @code{ml_form_radio_group}
+ * widget, which itself goes inside the form.
+ *
+ * @code{new_ml_form_radio} creates a new radio button widget. The
+ * radio button group into which this widget is being embedded is passed as the
+ * @code{group} parameter.
+ *
+ * After the form has been submitted, you can see which button was
+ * pressed by checking the value of the @code{ml_form_radio_group}
+ * widget, or by looking at the checked status of each individual
+ * radio button using @code{ml_form_radio_(set|get)_checked}.
+ *
+ * See also: @ref{new_ml_form(3)}, @ref{new_ml_form_radio_group(3)}.
+ */
+extern ml_form_radio new_ml_form_radio (pool pool, ml_form_radio_group group, const char *value);
+extern void ml_form_radio_set_checked (ml_form_radio w, int checked);
+extern int ml_form_radio_get_checked (ml_form_radio w);
+
+#endif /* ML_FORM_RADIO_H */
diff --git a/src/ml_form_radio_group.c b/src/ml_form_radio_group.c
new file mode 100644 (file)
index 0000000..31525cc
--- /dev/null
@@ -0,0 +1,144 @@
+/* Monolith group of radio buttons.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_radio_group.c,v 1.1 2002/08/30 14:28:47 rich Exp $
+ */
+
+#include "config.h"
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#include <pool.h>
+#include <hash.h>
+
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_form.h"
+#include "ml_form_input.h"
+#include "ml_form_radio.h"
+#include "ml_form_radio_group.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static void clear_value (void *);
+static void set_value (void *, const char *value);
+static const char *get_value (void *);
+
+struct ml_widget_operations form_radio_group_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_form_input_operations form_radio_group_input_ops =
+  {
+    clear_value: clear_value,
+    set_value: set_value,
+    get_value: get_value
+  };
+
+struct ml_form_radio_group
+{
+  struct ml_widget_operations *ops;
+  struct ml_form_input_operations *fops;
+  pool pool;                   /* Pool for allocations. */
+  const char *name;            /* Name of the input field. */
+  const char *value;           /* Value of the input field. */
+  ml_widget w;                 /* Packed widget. */
+  shash buttons;               /* Related buttons (value -> radio button). */
+};
+
+ml_form_radio_group
+new_ml_form_radio_group (pool pool, ml_form form)
+{
+  ml_form_radio_group w = pmalloc (pool, sizeof *w);
+
+  w->ops = &form_radio_group_ops;
+  w->fops = &form_radio_group_input_ops;
+  w->pool = pool;
+  w->value = 0;
+  w->w = 0;
+  w->buttons = new_shash (pool, ml_form_radio);
+
+  /* Register ourselves with the form. */
+  w->name = _ml_form_register_widget (form, w);
+
+  return w;
+}
+
+const char *
+_ml_form_radio_group_register (ml_form_radio_group w, ml_form_radio r,
+                              const char *value)
+{
+  shash_insert (w->buttons, value, r);
+  return w->name;
+}
+
+void
+ml_form_radio_group_pack (ml_form_radio_group w, ml_widget _w)
+{
+  w->w = _w;
+}
+
+static void
+clear_value (void *vw)
+{
+  ml_form_radio_group w = (ml_form_radio_group) vw;
+  int i;
+  vector values;
+  ml_form_radio r;
+
+  /* Uncheck all of the buttons. */
+  values = shash_values (w->buttons);
+  for (i = 0; i < vector_size (values); ++i)
+    {
+      vector_get (values, i, r);
+      ml_form_radio_set_checked (r, 0);
+    }
+  w->value = 0;
+}
+
+static void
+set_value (void *vw, const char *value)
+{
+  ml_form_radio_group w = (ml_form_radio_group) vw;
+  ml_form_radio r;
+
+  assert (value);
+  if (shash_get (w->buttons, value, r))
+    ml_form_radio_set_checked (r, 1);
+  w->value = value;
+}
+
+static const char *
+get_value (void *vw)
+{
+  ml_form_radio_group w = (ml_form_radio_group) vw;
+
+  return w->value;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_form_radio_group w = (ml_form_radio_group) vw;
+
+  if (w->w)
+    ml_widget_repaint (w->w, session, windowid, io);
+}
diff --git a/src/ml_form_radio_group.h b/src/ml_form_radio_group.h
new file mode 100644 (file)
index 0000000..5f35dee
--- /dev/null
@@ -0,0 +1,59 @@
+/* Monolith group of radio buttons.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_radio_group.h,v 1.1 2002/08/30 14:28:48 rich Exp $
+ */
+
+#ifndef ML_FORM_RADIO_GROUP_H
+#define ML_FORM_RADIO_GROUP_H
+
+#include "monolith.h"
+#include "ml_form.h"
+
+struct ml_form_radio_group;
+typedef struct ml_form_radio_group *ml_form_radio_group;
+
+struct ml_form_radio;
+
+/* Function: new_ml_form_radio_group - monolith group of radio buttons widget
+ * Function: ml_form_radio_group_pack
+ *
+ * A radio group is a widget which contains a group of related radio
+ * buttons. You cannot use radio buttons "naked" in a form, but instead
+ * must embed related buttons inside one of these widgets.
+ *
+ * @code{new_ml_form_radio_group} creates a new group widget.
+ *
+ * @code{ml_form_radio_group_pack} packs a single widget inside the
+ * radio group widget. A radio group can only contain a single widget,
+ * so if you call pack again, it will forget the previous widget.
+ * It is recommended that you pack either a flow layout or a table
+ * layout directly inside the radio group, and then pack the actual
+ * radio buttons inside that.
+ *
+ * See also: @ref{new_ml_form(3)}, @ref{new_ml_form_radio(3)},
+ * @ref{ml_form_input_get_value(3)}.
+ */
+extern ml_form_radio_group new_ml_form_radio_group (pool pool, ml_form form);
+extern void ml_form_radio_group_pack (ml_form_radio_group w, ml_widget);
+
+/* Radio buttons use this function to register themselves. It returns
+ * the name of the button.
+ */
+extern const char *_ml_form_radio_group_register (ml_form_radio_group w, struct ml_form_radio *, const char *value);
+
+#endif /* ML_FORM_RADIO_H */
diff --git a/src/ml_form_select.c b/src/ml_form_select.c
new file mode 100644 (file)
index 0000000..2d34813
--- /dev/null
@@ -0,0 +1,273 @@
+/* Monolith form select box input class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_select.c,v 1.4 2002/11/13 20:46:37 rich Exp $
+ */
+
+#include "config.h"
+
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_form_input.h"
+#include "ml_form_select.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property properties[];
+static void set_value (void *, const char *value);
+static void clear_value (void *);
+
+struct ml_widget_operations form_select_ops =
+  {
+    repaint: repaint,
+    properties: properties,
+  };
+
+struct ml_form_input_operations form_select_input_ops =
+  {
+    set_value: set_value,
+    get_value: 0,
+    clear_value: clear_value
+  };
+
+struct ml_form_select
+{
+  struct ml_widget_operations *ops;
+  struct ml_form_input_operations *fops;
+  pool pool;                   /* Pool for allocations. */
+  const char *name;            /* Name of the input field. */
+  int size;                    /* Size property. */
+  int multiple;                        /* Multiple property. */
+  vector options;              /* Options (vector of strings). */
+  vector selections;           /* Selected options (vector of int). */
+  int selected;                        /* Single selection. */
+};
+
+static struct ml_widget_property properties[] =
+  {
+    { name: "form.select.size",
+      offset: ml_offsetof (struct ml_form_select, size),
+      type: ML_PROP_INT },
+    { name: "form.select.multiple",
+      offset: ml_offsetof (struct ml_form_select, multiple),
+      type: ML_PROP_BOOL },
+    { 0 }
+  };
+
+ml_form_select
+new_ml_form_select (pool pool, ml_form form)
+{
+  ml_form_select w = pmalloc (pool, sizeof *w);
+
+  w->ops = &form_select_ops;
+  w->fops = &form_select_input_ops;
+  w->pool = pool;
+  w->size = 0;
+  w->multiple = 0;
+  w->options = new_vector (pool, const char *);
+  w->selections = 0;
+  w->selected = -1;
+
+  /* Register ourselves with the form. */
+  w->name = _ml_form_register_widget (form, w);
+
+  return w;
+}
+
+void
+ml_form_select_push_back (ml_form_select w, const char *option)
+{
+  vector_push_back (w->options, option);
+}
+
+const char *
+ml_form_select_pop_back (ml_form_select w)
+{
+  const char *option;
+
+  vector_pop_back (w->options, option);
+  return option;
+}
+
+void
+ml_form_select_push_front (ml_form_select w, const char *option)
+{
+  vector_push_front (w->options, option);
+}
+
+const char *
+ml_form_select_pop_front (ml_form_select w)
+{
+  const char *option;
+
+  vector_pop_front (w->options, option);
+  return option;
+}
+
+const char *
+ml_form_select_get (ml_form_select w, int option_index)
+{
+  const char *option;
+
+  vector_get (w->options, option_index, option);
+  return option;
+}
+
+void
+ml_form_select_insert (ml_form_select w, int option_index, const char *option)
+{
+  vector_insert (w->options, option_index, option);
+}
+
+void
+ml_form_select_replace (ml_form_select w, int option_index, const char *option)
+{
+  vector_replace (w->options, option_index, option);
+}
+
+void
+ml_form_select_erase (ml_form_select w, int option_index)
+{
+  vector_erase (w->options, option_index);
+}
+
+void
+ml_form_select_clear (ml_form_select w)
+{
+  vector_clear (w->options);
+}
+
+int
+ml_form_select_size (ml_form_select w)
+{
+  return vector_size (w->options);
+}
+
+void
+ml_form_select_set_selection (ml_form_select w, int option_index)
+{
+  if (!w->multiple)
+    w->selected = option_index;
+  else
+    abort ();
+}
+
+void
+ml_form_select_set_selections (ml_form_select w, vector selected)
+{
+  if (w->multiple)
+    w->selections = selected;
+  else
+    abort ();
+}
+
+int
+ml_form_select_get_selection (ml_form_select w)
+{
+  if (!w->multiple)
+    return w->selected;
+  else
+    abort ();
+}
+
+const vector
+ml_form_select_get_selections (ml_form_select w)
+{
+  if (w->multiple)
+    return w->selections;
+  else
+    abort ();
+}
+
+static void
+clear_value (void *vw)
+{
+  ml_form_select w = (ml_form_select) vw;
+
+  if (!w->multiple)
+    {
+      w->selected = -1;
+    }
+  else
+    {
+      int zero = 0;
+
+      w->selections = new_vector (w->pool, int);
+      vector_fill (w->selections, zero, vector_size (w->options));
+    }
+}
+
+static void
+set_value (void *vw, const char *value)
+{
+  ml_form_select w = (ml_form_select) vw;
+
+  sscanf (value, "%d", &w->selected);
+
+  if (w->multiple && w->selections &&
+      w->selected >= 0 && w->selected < vector_size (w->selections))
+    {
+      int one = 1;
+
+      vector_replace (w->selections, w->selected, one);
+    }
+}
+
+static inline int
+is_selected (ml_form_select w, int index)
+{
+  if (!w->multiple)
+    return index == w->selected;
+  else
+    {
+      if (w->selections && index < vector_size (w->selections))
+       {
+         int i;
+
+         vector_get (w->selections, index, i);
+         return i;
+       }
+      else
+       return 0;
+    }
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_form_select w = (ml_form_select) vw;
+  int i;
+  const char *option;
+
+  io_fprintf (io, "<select class=\"ml_form_select\" name=\"%s\"", w->name);
+  if (w->size) io_fprintf (io, " size=\"%d\"", w->size);
+  if (w->multiple) io_fprintf (io, " multiple=\"1\"");
+  io_fprintf (io, ">");
+
+  for (i = 0; i < vector_size (w->options); ++i)
+    {
+      vector_get (w->options, i, option);
+
+      io_fprintf (io, "<option value=\"%d\"", i);
+      if (is_selected (w, i))
+       io_fprintf (io, " selected=\"1\"");
+      io_fprintf (io, ">%s</option>\n", option);
+    }
+
+  io_fprintf (io, "</select>");
+}
diff --git a/src/ml_form_select.h b/src/ml_form_select.h
new file mode 100644 (file)
index 0000000..2d4617f
--- /dev/null
@@ -0,0 +1,115 @@
+/* Monolith form select box input class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_select.h,v 1.3 2002/11/02 18:53:47 rich Exp $
+ */
+
+#ifndef ML_FORM_SELECT_H
+#define ML_FORM_SELECT_H
+
+#include "monolith.h"
+#include "ml_form.h"
+
+struct ml_form_select;
+typedef struct ml_form_select *ml_form_select;
+
+/* Function: new_ml_form_select - monolith form select box input widget
+ * Function: ml_form_select_push_back
+ * Function: ml_form_select_pop_back
+ * Function: ml_form_select_push_front
+ * Function: ml_form_select_pop_front
+ * Function: ml_form_select_get
+ * Function: ml_form_select_insert
+ * Function: ml_form_select_replace
+ * Function: ml_form_select_erase
+ * Function: ml_form_select_clear
+ * Function: ml_form_select_size
+ * Function: ml_form_select_set_selection
+ * Function: ml_form_select_set_selections
+ * Function: ml_form_select_get_selection
+ * Function: ml_form_select_get_selections
+ *
+ * This is a select box for use in forms. It can appear in several
+ * ways: either as a drop-down menu, or as a selection box allowing
+ * single or multiple options to be selected.
+ *
+ * @code{new_ml_form_select} creates a new form select box input widget. The
+ * form into which this widget is being embedded is passed as the
+ * @code{form} parameter. The select box is created with no options,
+ * in drop-down mode (size 0), single choice (multiple 0).
+ *
+ * The following properties can be changed on form select box input widgets
+ * (see @ref{ml_widget_set_property(3)}):
+ *
+ * @code{form.select.size}: The size (number of rows)
+ * in the select box. If the size is 0, then the select box will be
+ * rendered as a drop-down menu. If the size is > 0, then the select
+ * box will be rendered as a scrolling list of choices.
+ *
+ * @code{form.select.multiple} (boolean): The multiple boolean
+ * property of the select box. If this is false (the default), then
+ * only a single option can be selected. If this is true, then multiple
+ * options can be selected by the user.
+ *
+ * To add options to the select box, use the @code{ml_form_select_push_back}
+ * and other access functions. These are modelled on the c2lib @code{vector_*}
+ * functions.
+ *
+ * To choose which option is selected first in a single selection select
+ * box, call @code{ml_form_select_set_selection}. For select boxes which
+ * allow multiple selections, prepare a @code{vector} of @code{int} which
+ * is at least as long as the number of options. Each element of the
+ * vector should be a boolean value saying whether the corresponding
+ * option is selected or not. Pass this to
+ * @code{ml_form_select_set_selections}.
+ *
+ * If the select box allows only single selections, then after the form
+ * has been submitted by the user, you can read back the index of the
+ * option which was selected using @code{ml_form_select_get_selection}.
+ * This returns -1 if nothing was selected.
+ *
+ * If the select box allows multiple selections, then call
+ * @code{ml_form_select_get_selections} which returns a vector
+ * of boolean values (@code{vector} of @code{int}) of exactly
+ * the same length as the number of options. This vector will
+ * contain true corresponding to every selected option. This
+ * function may also return @code{NULL}, indicating that
+ * nothing was selected (or the form wasn't submitted).
+ *
+ * See also: @ref{new_ml_form(3)}, @ref{ml_form_input_get_value(3)},
+ * @ref{vector_push_back(3)}, @ref{vector_pop_back(3)},
+ * @ref{vector_push_front(3)}, @ref{vector_pop_front(3)},
+ * @ref{vector_get(3)}, @ref{vector_insert(3)}, @ref{vector_replace(3)},
+ * @ref{vector_erase(3)}, @ref{vector_clear(3)}, @ref{vector_size(3)}.
+ */
+extern ml_form_select new_ml_form_select (pool pool, ml_form form);
+extern void ml_form_select_push_back (ml_form_select w, const char *option);
+extern const char *ml_form_select_pop_back (ml_form_select w);
+extern void ml_form_select_push_front (ml_form_select w, const char *option);
+extern const char *ml_form_select_pop_front (ml_form_select w);
+extern const char *ml_form_select_get (ml_form_select w, int option_index);
+extern void ml_form_select_insert (ml_form_select w, int option_index, const char *option);
+extern void ml_form_select_replace (ml_form_select w, int option_index, const char *option);
+extern void ml_form_select_erase (ml_form_select w, int option_index);
+extern void ml_form_select_clear (ml_form_select w);
+extern int ml_form_select_size (ml_form_select w);
+extern void ml_form_select_set_selection (ml_form_select w, int option_index);
+extern void ml_form_select_set_selections (ml_form_select w, vector selected);
+extern int ml_form_select_get_selection (ml_form_select w);
+extern const vector ml_form_select_get_selections (ml_form_select w);
+
+#endif /* ML_FORM_SELECT_H */
diff --git a/src/ml_form_submit.c b/src/ml_form_submit.c
new file mode 100644 (file)
index 0000000..1274487
--- /dev/null
@@ -0,0 +1,106 @@
+/* Monolith form submit button class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_submit.c,v 1.4 2003/01/11 16:39:10 rich Exp $
+ */
+
+#include "config.h"
+
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_smarttext.h"
+#include "ml_form_submit.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static void clear_value (void *);
+static void set_value (void *, const char *value);
+static const char *get_value (void *);
+
+struct ml_widget_operations form_submit_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_form_input_operations form_submit_input_ops =
+  {
+    clear_value: clear_value,
+    set_value: set_value,
+    get_value: get_value
+  };
+
+struct ml_form_submit
+{
+  struct ml_widget_operations *ops;
+  struct ml_form_input_operations *fops;
+  pool pool;                   /* Pool for allocations. */
+  const char *name;            /* Name of the input field. */
+  const char *value;           /* Value of the input field. */
+};
+
+ml_form_submit
+new_ml_form_submit (pool pool, ml_form form, const char *value)
+{
+  ml_form_submit w = pmalloc (pool, sizeof *w);
+
+  w->ops = &form_submit_ops;
+  w->fops = &form_submit_input_ops;
+  w->pool = pool;
+  w->value = value;
+
+  /* Register ourselves with the form. */
+  w->name = _ml_form_register_widget (form, w);
+
+  return w;
+}
+
+static void
+clear_value (void *vw)
+{
+  ml_form_submit w = (ml_form_submit) vw;
+
+  w->value = 0;
+}
+
+static void
+set_value (void *vw, const char *value)
+{
+  ml_form_submit w = (ml_form_submit) vw;
+
+  w->value = value;
+}
+
+static const char *
+get_value (void *vw)
+{
+  ml_form_submit w = (ml_form_submit) vw;
+
+  return w->value;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_form_submit w = (ml_form_submit) vw;
+
+  io_fprintf (io, "<input class=\"ml_form_submit\" "
+             "type=\"submit\" name=\"%s\" value=\"",
+             w->name);
+  if (w->value) ml_plaintext_print (io, w->value);
+  io_fputs ("\" />", io);
+}
diff --git a/src/ml_form_submit.h b/src/ml_form_submit.h
new file mode 100644 (file)
index 0000000..27dc7ee
--- /dev/null
@@ -0,0 +1,44 @@
+/* Monolith form submit button class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_submit.h,v 1.2 2002/08/29 17:34:18 rich Exp $
+ */
+
+#ifndef ML_FORM_SUBMIT_H
+#define ML_FORM_SUBMIT_H
+
+#include "monolith.h"
+#include "ml_form.h"
+
+struct ml_form_submit;
+typedef struct ml_form_submit *ml_form_submit;
+
+/* Function: new_ml_form_submit - monolith form submit button widget
+ *
+ * This is a submit button, which can only be used within forms (see
+ * @code{new_ml_form(3)}).
+ *
+ * @code{new_ml_form_submit} creates a new form submit button. The
+ * form into which this widget is being embedded is passed as the
+ * @code{form} parameter. The @code{value}
+ * parameter is what is displayed on the button.
+ *
+ * See also: @ref{new_ml_form(3)}, @ref{ml_form_input_get_value(3)}.
+ */
+extern ml_form_submit new_ml_form_submit (pool pool, ml_form form, const char *value);
+
+#endif /* ML_FORM_SUBMIT_H */
diff --git a/src/ml_form_text.c b/src/ml_form_text.c
new file mode 100644 (file)
index 0000000..e681e58
--- /dev/null
@@ -0,0 +1,157 @@
+/* Monolith form text input class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_text.c,v 1.5 2003/01/11 16:39:10 rich Exp $
+ */
+
+#include "config.h"
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_smarttext.h"
+#include "ml_form_input.h"
+#include "ml_form_text.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property properties[];
+static void clear_value (void *);
+static void set_value (void *, const char *value);
+static const char *get_value (void *);
+
+struct ml_widget_operations form_text_ops =
+  {
+    repaint: repaint,
+    properties: properties,
+  };
+
+struct ml_form_input_operations form_text_input_ops =
+  {
+    clear_value: clear_value,
+    set_value: set_value,
+    get_value: get_value
+  };
+
+struct ml_form_text
+{
+  struct ml_widget_operations *ops;
+  struct ml_form_input_operations *fops;
+  pool pool;                   /* Pool for allocations. */
+  const char *name;            /* Name of the input field. */
+  const char *value;           /* Value of the input field. */
+  int size;                    /* Displayed width. */
+  int maxlength;               /* Maximum number of characters. */
+  ml_form form;                        /* The form. */
+  int focus;                   /* If set, grab focus. */
+};
+
+static struct ml_widget_property properties[] =
+  {
+    { name: "form.text.size",
+      offset: ml_offsetof (struct ml_form_text, size),
+      type: ML_PROP_INT },
+    { name: "form.text.maxlength",
+      offset: ml_offsetof (struct ml_form_text, maxlength),
+      type: ML_PROP_INT },
+    { 0 }
+  };
+
+ml_form_text
+new_ml_form_text (pool pool, ml_form form)
+{
+  ml_form_text w = pmalloc (pool, sizeof *w);
+
+  w->ops = &form_text_ops;
+  w->fops = &form_text_input_ops;
+  w->pool = pool;
+  w->value = 0;
+  w->size = w->maxlength = -1;
+  w->form = form;
+  w->focus = 0;
+
+  /* Register ourselves with the form. */
+  w->name = _ml_form_register_widget (form, w);
+
+  return w;
+}
+
+void
+ml_form_text_focus (ml_form_text w)
+{
+  w->focus = 1;
+}
+
+static void
+clear_value (void *vw)
+{
+  ml_form_text w = (ml_form_text) vw;
+
+  w->value = 0;
+}
+
+static void
+set_value (void *vw, const char *value)
+{
+  ml_form_text w = (ml_form_text) vw;
+
+  assert (value);
+  w->value = value;
+}
+
+static const char *
+get_value (void *vw)
+{
+  ml_form_text w = (ml_form_text) vw;
+
+  return w->value;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_form_text w = (ml_form_text) vw;
+
+  io_fprintf (io, "<input class=\"ml_form_text\" name=\"%s\" value=\"",
+             w->name);
+  if (w->value) ml_plaintext_print (io, w->value);
+  io_fputc ('"', io);
+  if (w->size >= 0)
+    io_fprintf (io, " size=\"%d\"", w->size);
+  if (w->maxlength >= 0)
+    io_fprintf (io, " maxlength=\"%d\"", w->maxlength);
+  io_fputs (" />", io);
+
+  /* XXX It is quite likely this won't work. It looks like we need to
+   * provide an onLoad function in the window to do this reliably.
+   */
+  if (w->focus)
+    {
+      const char *form_name;
+
+      ml_widget_get_property (w->form, "form.name", form_name);
+
+      io_fprintf (io, "<script language=\"javascript\"><!--\n"
+                 "  document.%s.%s.focus ();\n"
+                 "//--></script>\n",
+                 form_name, w->name);
+    }
+}
diff --git a/src/ml_form_text.h b/src/ml_form_text.h
new file mode 100644 (file)
index 0000000..4822f4d
--- /dev/null
@@ -0,0 +1,60 @@
+/* Monolith form text input class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_text.h,v 1.3 2002/11/23 16:46:06 rich Exp $
+ */
+
+#ifndef ML_FORM_TEXT_H
+#define ML_FORM_TEXT_H
+
+#include "monolith.h"
+#include "ml_form.h"
+
+struct ml_form_text;
+typedef struct ml_form_text *ml_form_text;
+
+/* Function: new_ml_form_text - monolith form text input widget
+ * Function: ml_form_text_focus
+ *
+ * This is a single line text input field, which can only be used
+ * within forms (see @code{new_ml_form(3)}).
+ *
+ * @code{new_ml_form_text} creates a new form text input widget. The
+ * form into which this widget is being embedded is passed as the
+ * @code{form} parameter.
+ *
+ * The following properties can be changed on form text input widgets
+ * (see @ref{ml_widget_set_property(3)}):
+ *
+ * @code{form.text.size}: Size (ie. width) of the input box. The default is
+ * @code{-1} which is a browser-specific width.
+ *
+ * @code{form.text.maxlength}: Maximum number of characters which
+ * may be entered into the input box (do not rely on this: always
+ * check the length). The default is @code{-1} which means unlimited.
+ *
+ * If @code{ml_form_text_focus} is called on the input widget, then
+ * it will attempt to grab keyboard focus when displayed. (NB. This
+ * is a temporary function, very likely to change in future when I
+ * come up with a better way to handle focus).
+ *
+ * See also: @ref{new_ml_form(3)}, @ref{ml_form_input_get_value(3)}.
+ */
+extern ml_form_text new_ml_form_text (pool pool, ml_form form);
+extern void ml_form_text_focus (ml_form_text w);
+
+#endif /* ML_FORM_TEXT_H */
diff --git a/src/ml_form_textarea.c b/src/ml_form_textarea.c
new file mode 100644 (file)
index 0000000..85a2b3c
--- /dev/null
@@ -0,0 +1,114 @@
+/* Monolith form textarea class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_textarea.c,v 1.4 2003/01/11 16:39:10 rich Exp $
+ */
+
+#include "config.h"
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_smarttext.h"
+#include "ml_form_textarea.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static void clear_value (void *);
+static void set_value (void *, const char *value);
+static const char *get_value (void *);
+
+struct ml_widget_operations form_textarea_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_form_input_operations form_textarea_input_ops =
+  {
+    clear_value: clear_value,
+    set_value: set_value,
+    get_value: get_value
+  };
+
+struct ml_form_textarea
+{
+  struct ml_widget_operations *ops;
+  struct ml_form_input_operations *fops;
+  pool pool;                   /* Pool for allocations. */
+  int rows, cols;              /* Size of the textarea. */
+  const char *name;            /* Name of the input field. */
+  const char *value;           /* Value of the input field. */
+};
+
+ml_form_textarea
+new_ml_form_textarea (pool pool, ml_form form, int rows, int cols)
+{
+  ml_form_textarea w = pmalloc (pool, sizeof *w);
+
+  w->ops = &form_textarea_ops;
+  w->fops = &form_textarea_input_ops;
+  w->pool = pool;
+  w->rows = rows;
+  w->cols = cols;
+  w->value = 0;
+
+  /* Register ourselves with the form. */
+  w->name = _ml_form_register_widget (form, w);
+
+  return w;
+}
+
+static void
+clear_value (void *vw)
+{
+  ml_form_textarea w = (ml_form_textarea) vw;
+
+  w->value = 0;
+}
+
+static void
+set_value (void *vw, const char *value)
+{
+  ml_form_textarea w = (ml_form_textarea) vw;
+
+  assert (value);
+  w->value = value;
+}
+
+static const char *
+get_value (void *vw)
+{
+  ml_form_textarea w = (ml_form_textarea) vw;
+
+  return w->value;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_form_textarea w = (ml_form_textarea) vw;
+
+  io_fprintf (io, "<textarea class=\"ml_form_textarea\" "
+             "rows=\"%d\" cols=\"%d\" name=\"%s\">",
+             w->rows, w->cols, w->name);
+  if (w->value) ml_plaintext_print (io, w->value); /* XXX Correct? */
+  io_fputs ("</textarea>", io);
+}
diff --git a/src/ml_form_textarea.h b/src/ml_form_textarea.h
new file mode 100644 (file)
index 0000000..0006b10
--- /dev/null
@@ -0,0 +1,45 @@
+/* Monolith form textarea class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_form_textarea.h,v 1.2 2002/08/29 17:34:18 rich Exp $
+ */
+
+#ifndef ML_FORM_TEXTAREA_H
+#define ML_FORM_TEXTAREA_H
+
+#include "monolith.h"
+#include "ml_form.h"
+
+struct ml_form_textarea;
+typedef struct ml_form_textarea *ml_form_textarea;
+
+/* Function: new_ml_form_textarea - monolith form textarea widget
+ *
+ * This is a multiple line text input field, which can only be used
+ * within forms (see @code{new_ml_form(3)}).
+ *
+ * @code{new_ml_form_textarea} creates a new form textarea widget. The
+ * form into which this widget is being embedded is passed as the
+ * @code{form} parameter. The size of this input field is given by the
+ * @code{rows} and @code{cols} parameters (both measured in number of
+ * characters).
+ *
+ * See also: @ref{new_ml_form(3)}, @ref{ml_form_input_get_value(3)}
+ */
+extern ml_form_textarea new_ml_form_textarea (pool pool, ml_form form, int rows, int cols);
+
+#endif /* ML_FORM_TEXTAREA_H */
diff --git a/src/ml_heading.c b/src/ml_heading.c
new file mode 100644 (file)
index 0000000..28e136d
--- /dev/null
@@ -0,0 +1,86 @@
+/* Monolith heading class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_heading.c,v 1.2 2002/11/23 17:31:01 rich Exp $
+ */
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_smarttext.h"
+#include "ml_heading.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property properties[];
+
+struct ml_widget_operations heading_ops =
+  {
+    repaint: repaint,
+    properties: properties,
+  };
+
+struct ml_heading
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  int level;                   /* Heading level (1 - 6). */
+  const char *text;            /* Text to be displayed. */
+};
+
+static struct ml_widget_property properties[] =
+  {
+    { name: "text",
+      offset: ml_offsetof (struct ml_heading, text),
+      type: ML_PROP_STRING },
+    { name: "heading.level",
+      offset: ml_offsetof (struct ml_heading, level),
+      type: ML_PROP_INT },
+    { 0 },
+  };
+
+ml_heading
+new_ml_heading (pool pool, int level, const char *text)
+{
+  ml_heading w = pmalloc (pool, sizeof *w);
+
+  w->ops = &heading_ops;
+  w->pool = pool;
+  w->level = level;
+  w->text = text;
+
+  return w;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_heading w = (ml_heading) vw;
+
+  if (w->text)
+    {
+      io_fprintf (io, "<h%d class=\"ml_heading\">", w->level);
+      ml_plaintext_print (io, w->text);
+      io_fprintf (io, "</h%d>", w->level);
+    }
+}
diff --git a/src/ml_heading.h b/src/ml_heading.h
new file mode 100644 (file)
index 0000000..90513f3
--- /dev/null
@@ -0,0 +1,49 @@
+/* Monolith heading class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_heading.h,v 1.2 2003/01/11 16:39:10 rich Exp $
+ */
+
+#ifndef ML_HEADING_H
+#define ML_HEADING_H
+
+#include <ml_widget.h>
+
+struct ml_heading;
+typedef struct ml_heading *ml_heading;
+
+/* Function: new_ml_heading - monolith heading widget
+ *
+ * Headings are simple text strings which are displayed as headings
+ * for sections of a document or page. There are six levels of
+ * heading, from 1 to 6, with 1 being the top level.
+ *
+ * @code{new_ml_heading} creates a new heading widget. The
+ * @code{level} parameter is the heading level (a number from
+ * 1 to 6). The @code{text} parameter is the text which will
+ * be displayed in the heading.
+ *
+ * The following properties can be changed on heading widgets (see
+ * @code{ml_widget_set_property(3)}):
+ *
+ * @code{text}: The text displayed in the heading.
+ *
+ * @code{heading.level}: The level.
+ */
+extern ml_heading new_ml_heading (pool, int level, const char *text);
+
+#endif /* ML_HEADING_H */
diff --git a/src/ml_horizontal_layout.c b/src/ml_horizontal_layout.c
new file mode 100644 (file)
index 0000000..974121d
--- /dev/null
@@ -0,0 +1,149 @@
+/* Monolith horizontal layout class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_horizontal_layout.c,v 1.1 2002/11/03 09:39:46 rich Exp $
+ */
+
+#include "config.h"
+
+#include <pthr_iolib.h>
+
+#include "ml_widget.h"
+#include "monolith.h"
+#include "ml_horizontal_layout.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations horizontal_layout_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_horizontal_layout
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  vector v;                    /* Vector of widgets. */
+};
+
+ml_horizontal_layout
+new_ml_horizontal_layout (pool pool)
+{
+  ml_horizontal_layout w = pmalloc (pool, sizeof *w);
+
+  w->ops = &horizontal_layout_ops;
+  w->pool = pool;
+  w->v = new_vector (pool, ml_widget);
+
+  return w;
+}
+
+void
+ml_horizontal_layout_push_back (ml_horizontal_layout w, ml_widget _w)
+{
+  vector_push_back (w->v, _w);
+}
+
+void
+ml_horizontal_layout_pack (ml_horizontal_layout w, ml_widget _w)
+{
+  vector_push_back (w->v, _w);
+}
+
+ml_widget
+ml_horizontal_layout_pop_back (ml_horizontal_layout w)
+{
+  ml_widget _w;
+
+  vector_pop_back (w->v, _w);
+  return _w;
+}
+
+void
+ml_horizontal_layout_push_front (ml_horizontal_layout w, ml_widget _w)
+{
+  vector_push_front (w->v, _w);
+}
+
+ml_widget
+ml_horizontal_layout_pop_front (ml_horizontal_layout w)
+{
+  ml_widget _w;
+
+  vector_pop_front (w->v, _w);
+  return _w;
+}
+
+ml_widget
+ml_horizontal_layout_get (ml_horizontal_layout w, int i)
+{
+  ml_widget _w;
+
+  vector_get (w->v, i, _w);
+  return _w;
+}
+
+void
+ml_horizontal_layout_insert (ml_horizontal_layout w, int i, ml_widget _w)
+{
+  vector_insert (w->v, i, _w);
+}
+
+void
+ml_horizontal_layout_replace (ml_horizontal_layout w, int i, ml_widget _w)
+{
+  vector_replace (w->v, i, _w);
+}
+
+void
+ml_horizontal_layout_erase (ml_horizontal_layout w, int i)
+{
+  vector_erase (w->v, i);
+}
+
+void
+ml_horizontal_layout_clear (ml_horizontal_layout w)
+{
+  vector_clear (w->v);
+}
+
+int
+ml_horizontal_layout_size (ml_horizontal_layout w)
+{
+  return vector_size (w->v);
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_horizontal_layout w = (ml_horizontal_layout) vw;
+  int i;
+
+  io_fputs ("<table><tr>", io);
+
+  for (i = 0; i < vector_size (w->v); ++i)
+    {
+      ml_widget _w;
+
+      vector_get (w->v, i, _w);
+      io_fputs ("<td valign=\"top\">", io);
+      ml_widget_repaint (_w, session, windowid, io);
+      io_fputs ("</td>\n", io);
+    }
+
+  io_fputs ("</tr></table>", io);
+}
diff --git a/src/ml_horizontal_layout.h b/src/ml_horizontal_layout.h
new file mode 100644 (file)
index 0000000..9d79e90
--- /dev/null
@@ -0,0 +1,74 @@
+/* Monolith horizontal layout class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_horizontal_layout.h,v 1.1 2002/11/03 09:39:46 rich Exp $
+ */
+
+#ifndef ML_HORIZONTAL_LAYOUT_H
+#define ML_HORIZONTAL_LAYOUT_H
+
+#include <ml_widget.h>
+
+struct ml_horizontal_layout;
+typedef struct ml_horizontal_layout *ml_horizontal_layout;
+
+/* Function: new_ml_horizontal_layout - monolith horizontal_layout widget
+ * Function: ml_horizontal_layout_push_back
+ * Function: ml_horizontal_layout_pop_back
+ * Function: ml_horizontal_layout_push_front
+ * Function: ml_horizontal_layout_pop_front
+ * Function: ml_horizontal_layout_get
+ * Function: ml_horizontal_layout_insert
+ * Function: ml_horizontal_layout_replace
+ * Function: ml_horizontal_layout_erase
+ * Function: ml_horizontal_layout_clear
+ * Function: ml_horizontal_layout_size
+ * Function: ml_horizontal_layout_pack
+ *
+ * A horizontal layout widget is a simple type of layout which can be
+ * thought of as a specialised one-row table.
+ *
+ * @code{new_ml_horizontal_layout} creates a new horizontal layout widget.
+ *
+ * Underlying the horizontal layout widget is a simple c2lib vector, and the
+ * other access functions use the same notation as the equivalent
+ * c2lib @code{vector_*} functions. Go to the SEE ALSO section
+ * below to see how to manipulate widgets within a horizontal layout.
+ *
+ * @code{ml_horizontal_layout_pack} is equivalent to
+ * @code{ml_horizontal_layout_push_back}: it appends the widget to the
+ * end of the current vector of widgets.
+ *
+ * See also: @ref{vector_push_back(3)}, @ref{vector_pop_back(3)},
+ * @ref{vector_push_front(3)}, @ref{vector_pop_front(3)},
+ * @ref{vector_get(3)}, @ref{vector_insert(3)}, @ref{vector_replace(3)},
+ * @ref{vector_erase(3)}, @ref{vector_clear(3)}, @ref{vector_size(3)}.
+ */
+extern ml_horizontal_layout new_ml_horizontal_layout (pool pool);
+extern void ml_horizontal_layout_push_back (ml_horizontal_layout, ml_widget);
+extern ml_widget ml_horizontal_layout_pop_back (ml_horizontal_layout);
+extern void ml_horizontal_layout_push_front (ml_horizontal_layout, ml_widget);
+extern ml_widget ml_horizontal_layout_pop_front (ml_horizontal_layout);
+extern ml_widget ml_horizontal_layout_get (ml_horizontal_layout, int i);
+extern void ml_horizontal_layout_insert (ml_horizontal_layout, int i, ml_widget);
+extern void ml_horizontal_layout_replace (ml_horizontal_layout, int i, ml_widget);
+extern void ml_horizontal_layout_erase (ml_horizontal_layout, int i);
+extern void ml_horizontal_layout_clear (ml_horizontal_layout);
+extern int ml_horizontal_layout_size (ml_horizontal_layout);
+extern void ml_horizontal_layout_pack (ml_horizontal_layout, ml_widget);
+
+#endif /* ML_HORIZONTAL_LAYOUT_H */
diff --git a/src/ml_iframe.c b/src/ml_iframe.c
new file mode 100644 (file)
index 0000000..fa0aca7
--- /dev/null
@@ -0,0 +1,112 @@
+/* Monolith iframe class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_iframe.c,v 1.1 2002/11/08 23:19:14 rich Exp $
+ */
+
+#include "config.h"
+
+#include <pool.h>
+#include <pstring.h>
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_iframe.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property properties[];
+
+struct ml_widget_operations iframe_ops =
+  {
+    repaint: repaint,
+    properties: properties,
+  };
+
+struct ml_iframe
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  const char *action_id;       /* Action. */
+  ml_widget non_frame_widget;  /* Alternative for non-frames browsers. */
+  int width, height;           /* Width, height. */
+  const char *clazz;           /* Class. */
+  const char *scrolling;       /* "yes", "no" or "auto". */
+};
+
+static struct ml_widget_property properties[] =
+  {
+    { name: "width",
+      offset: ml_offsetof (struct ml_iframe, width),
+      type: ML_PROP_INT },
+    { name: "height",
+      offset: ml_offsetof (struct ml_iframe, height),
+      type: ML_PROP_INT },
+    { name: "class",
+      offset: ml_offsetof (struct ml_iframe, clazz),
+      type: ML_PROP_STRING },
+    { name: "scrolling",
+      offset: ml_offsetof (struct ml_iframe, scrolling),
+      type: ML_PROP_STRING },
+    { 0 }
+  };
+
+ml_iframe
+new_ml_iframe (pool pool, void (*fn) (ml_session, void *),
+              ml_session session, void *data,
+              ml_widget non_frame_widget)
+{
+  ml_iframe w = pmalloc (pool, sizeof *w);
+
+  w->ops = &iframe_ops;
+  w->pool = pool;
+  w->clazz = 0;
+  w->scrolling = 0;
+  w->width = w->height = 0;
+  w->non_frame_widget = non_frame_widget;
+
+  /* Register the callback. */
+  w->action_id = ml_register_action (session, fn, data);
+
+  return w;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_iframe w = (ml_iframe) vw;
+
+  io_fprintf (io, "<iframe src=\"%s?ml_action=%s&ml_window=%s\"",
+             ml_session_script_name (session), w->action_id, windowid);
+
+  if (w->clazz)
+    io_fprintf (io, " class=\"%s\"", w->clazz);
+  if (w->width)
+    io_fprintf (io, " width=\"%d\"", w->width);
+  if (w->height)
+    io_fprintf (io, " height=\"%d\"", w->height);
+  if (w->scrolling)
+    io_fprintf (io, " scrolling=\"%s\"", w->scrolling);
+  io_fputs (">", io);
+
+  if (w->non_frame_widget)
+    ml_widget_repaint (w->non_frame_widget, session, windowid, io);
+  else
+    io_fputs ("Your browser does not support frames.", io);
+
+  io_fputs ("</iframe>", io);
+}
diff --git a/src/ml_iframe.h b/src/ml_iframe.h
new file mode 100644 (file)
index 0000000..3fb93e5
--- /dev/null
@@ -0,0 +1,60 @@
+/* Monolith iframe class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_iframe.h,v 1.1 2002/11/08 23:19:14 rich Exp $
+ */
+
+#ifndef ML_IFRAME_H
+#define ML_IFRAME_H
+
+#include "monolith.h"
+#include "ml_widget.h"
+
+struct ml_iframe;
+typedef struct ml_iframe *ml_iframe;
+
+/* Function: new_ml_iframe - monolith iframe widget
+ *
+ * An iframe ("inline frame") is a scrollable, independent window
+ * embedded within the application. Note that not all browsers support
+ * iframes.
+ *
+ * @code{new_ml_iframe} creates a new iframe widget. The @code{fn}
+ * argument is a function which is called back when the window inside
+ * the iframe is drawn. This function must create a window (see
+ * @ref{new_ml_window(3)}). The function is called with two
+ * parameters, @code{session} and an opaque @code{data} pointer.
+ * Since not all browsers support frames, you may specify a
+ * widget which is displayed to these users by specifying the
+ * @code{non_frame_widget} parameter. If @code{non_frame_widget} is
+ * set to @code{NULL} then some generic text is displayed instead.
+ *
+ * The following properties can be changed on buttons (see
+ * @ref{ml_widget_set_property(3)}):
+ *
+ * @code{class}: The stylesheet class.
+ *
+ * @code{scrolling}: Can be set to @code{"yes"}, @code{"no"} or
+ * @code{"auto"} (the default) to control how scrollbars are
+ * displayed around the iframe.
+ *
+ * @code{width}, @code{height}: Control the width and height of the
+ * @code{iframe} in pixels.
+ */
+extern ml_iframe new_ml_iframe (pool pool, void (*fn) (ml_session, void *), ml_session session, void *data, ml_widget non_frame_widget);
+
+#endif /* ML_IFRAME_H */
diff --git a/src/ml_image.c b/src/ml_image.c
new file mode 100644 (file)
index 0000000..ce62c1d
--- /dev/null
@@ -0,0 +1,74 @@
+/* Monolith image class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_image.c,v 1.2 2002/10/30 21:03:03 rich Exp $
+ */
+
+#include "config.h"
+
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_image.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property properties[];
+
+struct ml_widget_operations image_ops =
+  {
+    repaint: repaint,
+    properties: properties,
+  };
+
+struct ml_image
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  const char *src;             /* Source for the image. */
+};
+
+static struct ml_widget_property properties[] =
+  {
+    { name: "image",
+      offset: ml_offsetof (struct ml_image, src),
+      type: ML_PROP_STRING },
+    { 0 }
+  };
+
+ml_image
+new_ml_image (pool pool, const char *src)
+{
+  ml_image w = pmalloc (pool, sizeof *w);
+
+  w->ops = &image_ops;
+  w->pool = pool;
+  w->src = src;
+
+  return w;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_image w = (ml_image) vw;
+
+  if (w->src)
+    {
+      io_fprintf (io, "<img src=\"%s\" />", w->src);
+    }
+}
diff --git a/src/ml_image.h b/src/ml_image.h
new file mode 100644 (file)
index 0000000..e61cab7
--- /dev/null
@@ -0,0 +1,52 @@
+/* Monolith image class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_image.h,v 1.2 2002/10/30 21:03:03 rich Exp $
+ */
+
+#ifndef ML_IMAGE_H
+#define ML_IMAGE_H
+
+#include "monolith.h"
+
+struct ml_image;
+typedef struct ml_image *ml_image;
+
+/* Function: new_ml_image - monolith image widget
+ *
+ * An image widget displays a graphical image, taken from a
+ * particular source (in fact, it corresponds almost exactly
+ * to the HTML @code{<img/>} element).
+ *
+ * The current implementation of @code{ml_image} is rather
+ * immature. In future we will support image sizes, alt, title,
+ * longdesc, and so on.
+ *
+ * @code{new_ml_image} creates a new image. You should supply
+ * the @code{src} (source) for the image, which is an absolute or
+ * relative link. If @code{src} is set to @code{NULL} then no
+ * image is displayed.
+ *
+ * The following properties can be changed on images (see
+ * @ref{ml_widget_set_property(3)}):
+ *
+ * @code{image}: The source of the image. You may set this to
+ * @code{NULL} to display no image at all.
+ */
+extern ml_image new_ml_image (pool pool, const char *image);
+
+#endif /* ML_IMAGE_H */
diff --git a/src/ml_label.c b/src/ml_label.c
new file mode 100644 (file)
index 0000000..d1442d2
--- /dev/null
@@ -0,0 +1,71 @@
+/* Monolith label class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_label.c,v 1.2 2002/10/30 21:03:03 rich Exp $
+ */
+
+#include "config.h"
+
+#include <pthr_iolib.h>
+
+#include "ml_widget.h"
+#include "monolith.h"
+#include "ml_label.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property properties[];
+
+struct ml_widget_operations label_ops =
+  {
+    repaint: repaint,
+    properties: properties,
+  };
+
+struct ml_label
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  const char *text;            /* HTML printed for the label. */
+};
+
+static struct ml_widget_property properties[] =
+  {
+    { name: "text",
+      offset: ml_offsetof (struct ml_label, text),
+      type: ML_PROP_STRING },
+    { 0 }
+  };
+
+ml_label
+new_ml_label (pool pool, const char *text)
+{
+  ml_label w = pmalloc (pool, sizeof *w);
+
+  w->ops = &label_ops;
+  w->pool = pool;
+  w->text = text;
+
+  return w;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_label w = (ml_label) vw;
+
+  if (w->text) io_fputs (w->text, io);
+}
diff --git a/src/ml_label.h b/src/ml_label.h
new file mode 100644 (file)
index 0000000..5754044
--- /dev/null
@@ -0,0 +1,45 @@
+/* Monolith label class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_label.h,v 1.2 2002/10/30 21:03:03 rich Exp $
+ */
+
+#ifndef ML_LABEL_H
+#define ML_LABEL_H
+
+struct ml_label;
+typedef struct ml_label *ml_label;
+
+/* Function: new_ml_label - monolith label widget
+ *
+ * A label widget is a generic piece of HTML.
+ *
+ * @code{new_ml_label} creates a new label widget.
+ *
+ * The following properties can be changed on labels (see
+ * @ref{ml_widget_set_property(3)}):
+ *
+ * @code{text}: The HTML associated
+ * with the label. Note that the text string must be either
+ * static, or already allocated in the label's pool, or allocated
+ * in a pool with a longer lifetime than the label. If the text
+ * is set to @code{NULL} then this has the same effect as setting
+ * the text to the empty string.
+ */
+extern ml_label new_ml_label (pool pool, const char *text);
+
+#endif /* ML_LABEL_H */
diff --git a/src/ml_menu.c b/src/ml_menu.c
new file mode 100644 (file)
index 0000000..9ee27dc
--- /dev/null
@@ -0,0 +1,461 @@
+/* Monolith menubar, menu, menuitem classes.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_menu.c,v 1.2 2003/01/11 16:39:10 rich Exp $
+ */
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_smarttext.h"
+#include "ml_menu.h"
+
+static void menubar_repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property menubar_properties[];
+
+struct ml_widget_operations menubar_ops =
+  {
+    repaint: menubar_repaint,
+    properties: menubar_properties,
+  };
+
+struct ml_menubar
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  vector menus;                        /* Menus contained here. */
+  ml_widget w;                 /* Widget for main application area. */
+};
+
+static struct ml_widget_property menubar_properties[] =
+  {
+    { 0 }
+  };
+
+struct menu
+{
+  const char *name;
+  ml_menu menu;
+};
+
+static void menu_repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property menu_properties[];
+
+struct ml_widget_operations menu_ops =
+  {
+    repaint: menu_repaint,
+    properties: menu_properties,
+  };
+
+struct ml_menu
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  vector items;                        /* Menu items. */
+};
+
+static struct ml_widget_property menu_properties[] =
+  {
+    { 0 }
+  };
+
+static void menuitem_button_repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property menuitem_button_properties[];
+
+struct ml_widget_operations menuitem_button_ops =
+  {
+    repaint: menuitem_button_repaint,
+    properties: menuitem_button_properties,
+  };
+
+struct ml_menuitem_button
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  const char *text;            /* Text on the button. */
+  const char *action_id;       /* Action. */
+  const char *title;           /* Tooltip. */
+};
+
+static struct ml_widget_property menuitem_button_properties[] =
+  {
+    { 0 }
+  };
+
+static void menuitem_separator_repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property menuitem_separator_properties[];
+
+struct ml_widget_operations menuitem_separator_ops =
+  {
+    repaint: menuitem_separator_repaint,
+    properties: menuitem_separator_properties,
+  };
+
+struct ml_menuitem_separator
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+};
+
+static struct ml_widget_property menuitem_separator_properties[] =
+  {
+    { 0 }
+  };
+
+ml_menubar
+new_ml_menubar (pool pool)
+{
+  ml_menubar w = pmalloc (pool, sizeof *w);
+
+  w->ops = &menubar_ops;
+  w->pool = pool;
+  w->menus = new_vector (pool, struct menu);
+  w->w = 0;
+
+  return w;
+}
+
+void
+ml_menubar_push_back (ml_menubar w, const char *name, ml_menu menu)
+{
+  struct menu m = { name, menu };
+
+  vector_push_back (w->menus, m);
+}
+
+ml_menu
+ml_menubar_pop_back (ml_menubar w)
+{
+  struct menu m;
+
+  vector_pop_back (w->menus, m);
+  return m.menu;
+}
+
+void
+ml_menubar_push_front (ml_menubar w, const char *name, ml_menu menu)
+{
+  struct menu m = { name, menu };
+
+  vector_push_front (w->menus, m);
+}
+
+ml_menu
+ml_menubar_pop_front (ml_menubar w)
+{
+  struct menu m;
+
+  vector_pop_front (w->menus, m);
+  return m.menu;
+}
+
+ml_menu
+ml_menubar_get (ml_menubar w, int i)
+{
+  struct menu m;
+
+  vector_get (w->menus, i, m);
+  return m.menu;
+}
+
+void
+ml_menubar_insert (ml_menubar w, int i, const char *name, ml_menu menu)
+{
+  struct menu m = { name, menu };
+
+  vector_insert (w->menus, i, m);
+}
+
+void
+ml_menubar_replace (ml_menubar w, int i, const char *name, ml_menu menu)
+{
+  struct menu m = { name, menu };
+
+  vector_replace (w->menus, i, m);
+}
+
+void
+ml_menubar_erase (ml_menubar w, int i)
+{
+  vector_erase (w->menus, i);
+}
+
+void
+ml_menubar_clear (ml_menubar w)
+{
+  vector_clear (w->menus);
+}
+
+int
+ml_menubar_size (ml_menubar w)
+{
+  return vector_size (w->menus);
+}
+
+void
+ml_menubar_pack (ml_menubar w, ml_widget _w)
+{
+  w->w = _w;
+}
+
+static void
+menubar_repaint (void *vw, ml_session session,
+                const char *windowid, io_handle io)
+{
+  ml_menubar w = (ml_menubar) vw;
+  struct menu m;
+  int i;
+
+  io_fputs ("<table class=\"ml_menubar\"><tr>", io);
+
+  for (i = 0; i < vector_size (w->menus); ++i)
+    {
+      vector_get (w->menus, i, m);
+
+      io_fprintf (io, "<td class=\"ml_menubar_item\">%s", m.name);
+      menu_repaint (m.menu, session, windowid, io);
+      io_fputs ("</td>\n", io);
+    }
+
+  io_fputs ("<td width=\"100%\"></td></tr></table>", io);
+
+  if (w->w) ml_widget_repaint (w->w, session, windowid, io);
+}
+
+ml_menu
+new_ml_menu (pool pool)
+{
+  ml_menu w = pmalloc (pool, sizeof *w);
+
+  w->ops = &menu_ops;
+  w->pool = pool;
+  w->items = new_vector (pool, ml_widget);
+
+  return w;
+}
+
+void
+ml_menu_push_back (ml_menu w, ml_widget menu_or_menuitem)
+{
+  vector_push_back (w->items, menu_or_menuitem);
+}
+
+void
+ml_menu_push_back_button (ml_menu w, const char *text, void (*fn) (ml_session, void *), ml_session session, void *data)
+{
+  ml_menuitem_button b = new_ml_menuitem_button (w->pool, text);
+
+  ml_menuitem_button_set_callback (b, fn, session, data);
+  vector_push_back (w->items, b);
+}
+
+void
+ml_menu_push_back_inactive_button (ml_menu w, const char *text)
+{
+  ml_menuitem_button b = new_ml_menuitem_button (w->pool, text);
+
+  vector_push_back (w->items, b);
+}
+
+void
+ml_menu_push_back_separator (ml_menu w)
+{
+  ml_menuitem_separator b = new_ml_menuitem_separator (w->pool);
+
+  vector_push_back (w->items, b);
+}
+
+ml_widget
+ml_menu_pop_back (ml_menu w)
+{
+  ml_widget menu_or_menuitem;
+
+  vector_pop_back (w->items, menu_or_menuitem);
+  return menu_or_menuitem;
+}
+
+void
+ml_menu_push_front (ml_menu w, ml_widget menu_or_menuitem)
+{
+  vector_push_front (w->items, menu_or_menuitem);
+}
+
+ml_widget
+ml_menu_pop_front (ml_menu w)
+{
+  ml_widget menu_or_menuitem;
+
+  vector_pop_front (w->items, menu_or_menuitem);
+  return menu_or_menuitem;
+}
+
+ml_widget
+ml_menu_get (ml_menu w, int i)
+{
+  ml_widget menu_or_menuitem;
+
+  vector_get (w->items, i, menu_or_menuitem);
+  return menu_or_menuitem;
+}
+
+void
+ml_menu_insert (ml_menu w, int i, ml_widget menu_or_menuitem)
+{
+  vector_insert (w->items, i, menu_or_menuitem);
+}
+
+void
+ml_menu_replace (ml_menu w, int i, ml_widget menu_or_menuitem)
+{
+  vector_replace (w->items, i, menu_or_menuitem);
+}
+
+void
+ml_menu_erase (ml_menu w, int i)
+{
+  vector_erase (w->items, i);
+}
+
+void
+ml_menu_clear (ml_menu w)
+{
+  vector_clear (w->items);
+}
+
+int
+ml_menu_size (ml_menu w)
+{
+  return vector_size (w->items);
+}
+
+void
+ml_menu_pack (ml_menu w, ml_widget menu_or_menuitem)
+{
+  vector_push_back (w->items, menu_or_menuitem);
+}
+
+static void
+menu_repaint (void *vw, ml_session session,
+             const char *windowid, io_handle io)
+{
+  ml_menu w = (ml_menu) vw;
+  int i;
+  ml_widget m;
+
+  io_fputs ("<ul>", io);
+
+  for (i = 0; i < vector_size (w->items); ++i)
+    {
+      vector_get (w->items, i, m);
+
+      io_fputs ("<li>", io);
+      ml_widget_repaint (m, session, windowid, io);
+      io_fputs ("</li>\n", io);
+    }
+
+  io_fputs ("</ul>\n", io);
+}
+
+ml_menuitem_button
+new_ml_menuitem_button (pool pool, const char *text)
+{
+  ml_menuitem_button w = pmalloc (pool, sizeof *w);
+
+  w->ops = &menuitem_button_ops;
+  w->pool = pool;
+  w->text = text;
+  w->action_id = 0;
+  w->title = 0;
+
+  return w;
+}
+
+void
+ml_menuitem_button_set_callback (ml_menuitem_button w,
+                                void (*fn) (ml_session, void *),
+                                ml_session session, void *data)
+{
+  if (w->action_id)
+    ml_unregister_action (session, w->action_id);
+  w->action_id = 0;
+
+  if (fn)
+    w->action_id = ml_register_action (session, fn, data);
+}
+
+static void
+menuitem_button_repaint (void *vw, ml_session session,
+                        const char *windowid, io_handle io)
+{
+  ml_menuitem_button w = (ml_menuitem_button) vw;
+
+  if (w->text)
+    {
+      if (w->action_id)
+       {
+         pool tmp = new_subpool (w->pool);
+
+         const char *link =
+           psprintf (tmp, "%s?ml_action=%s&ml_window=%s",
+                     ml_session_script_name (session),
+                     w->action_id,
+                     windowid);
+
+         io_fprintf (io, "<a href=\"%s\"", link);
+
+         if (w->title)
+           {
+             io_fputs (" title=\"", io);
+             ml_plaintext_print (io, w->title);
+             io_fputc ('"', io);
+           }
+
+         io_fprintf (io, ">%s</a>", w->text);
+
+         delete_pool (tmp);
+       }
+      else
+       io_fprintf (io, "<span>%s</span>", w->text);
+    }
+}
+
+ml_menuitem_separator
+new_ml_menuitem_separator (pool pool)
+{
+  ml_menuitem_separator w = pmalloc (pool, sizeof *w);
+
+  w->ops = &menuitem_separator_ops;
+  w->pool = pool;
+
+  return w;
+}
+
+static void
+menuitem_separator_repaint (void *vw, ml_session session,
+                           const char *windowid, io_handle io)
+{
+  io_fputs ("<hr />", io);
+}
diff --git a/src/ml_menu.h b/src/ml_menu.h
new file mode 100644 (file)
index 0000000..60c8f64
--- /dev/null
@@ -0,0 +1,207 @@
+/* Monolith menubar, menu, menuitem classes.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_menu.h,v 1.1 2002/11/30 15:55:47 rich Exp $
+ */
+
+/* Note that this file contains all classes related to menus, menubars,
+ * menu items, etc.
+ */
+
+#ifndef ML_MENU_H
+#define ML_MENU_H
+
+#include "monolith.h"
+#include "ml_widget.h"
+
+struct ml_menubar;
+typedef struct ml_menubar *ml_menubar;
+
+struct ml_menu;
+typedef struct ml_menu *ml_menu;
+
+struct ml_menuitem_button;
+typedef struct ml_menuitem_button *ml_menuitem_button;
+
+struct ml_menuitem_separator;
+typedef struct ml_menuitem_separator *ml_menuitem_separator;
+
+/* Function: new_ml_menubar - monolith menu bar
+ * Function: ml_menubar_push_back
+ * Function: ml_menubar_pop_back
+ * Function: ml_menubar_push_front
+ * Function: ml_menubar_pop_front
+ * Function: ml_menubar_get
+ * Function: ml_menubar_insert
+ * Function: ml_menubar_replace
+ * Function: ml_menubar_erase
+ * Function: ml_menubar_clear
+ * Function: ml_menubar_size
+ * Function: ml_menubar_pack
+ *
+ * The monolith menubar widget is part of monolith's support for
+ * pull-down menus. The menubar appears along the top of the screen.
+ * Normally you would pack it directly into a window
+ * (see @ref{ml_window_pack(3)}), and then pack the main screen
+ * of the application into the menubar, using @code{ml_menubar_pack}.
+ *
+ * To populate the menubar with menus, you need to create @code{ml_menu}
+ * objects and insert them into the menubar using the functions described
+ * in this manpage. See @ref{new_ml_menu(3)} for details on how to create
+ * new menu objects.
+ *
+ * @code{new_ml_menubar} creates a new, empty menubar widget.
+ *
+ * Underlying the menubar is a simple c2lib vector, and the other
+ * access functions use the same notation as the equivalent
+ * c2lib @code{vector_*} functions. Go the SEE ALSO section below
+ * to see how to manipulate menu items within a menubar.
+ *
+ * @code{ml_menubar_pack} packs a widget into the body area of the
+ * menubar. Normally you would pack the main screen of the application
+ * here.
+ *
+ * See also: @ref{new_ml_menu(3)},
+ * @ref{vector_push_back(3)}, @ref{vector_pop_back(3)},
+ * @ref{vector_push_front(3)}, @ref{vector_pop_front(3)},
+ * @ref{vector_get(3)}, @ref{vector_insert(3)}, @ref{vector_replace(3)},
+ * @ref{vector_erase(3)}, @ref{vector_clear(3)}, @ref{vector_size(3)}.
+ */
+extern ml_menubar new_ml_menubar (pool pool);
+extern void ml_menubar_push_back (ml_menubar, const char *name, ml_menu);
+extern ml_menu ml_menubar_pop_back (ml_menubar);
+extern void ml_menubar_push_front (ml_menubar, const char *name, ml_menu);
+extern ml_menu ml_menubar_pop_front (ml_menubar);
+extern ml_menu ml_menubar_get (ml_menubar, int i);
+extern void ml_menubar_insert (ml_menubar, int i, const char *name, ml_menu);
+extern void ml_menubar_replace (ml_menubar, int i, const char *name, ml_menu);
+extern void ml_menubar_erase (ml_menubar, int i);
+extern void ml_menubar_clear (ml_menubar);
+extern int ml_menubar_size (ml_menubar);
+extern void ml_menubar_pack (ml_menubar, ml_widget);
+
+/* Function: new_ml_menu - monolith pull down menu
+ * Function: ml_menu_push_back
+ * Function: ml_menu_push_back_button
+ * Function: ml_menu_push_back_inactive_button
+ * Function: ml_menu_push_back_separator
+ * Function: ml_menu_pop_back
+ * Function: ml_menu_push_front
+ * Function: ml_menu_pop_front
+ * Function: ml_menu_get
+ * Function: ml_menu_insert
+ * Function: ml_menu_replace
+ * Function: ml_menu_erase
+ * Function: ml_menu_clear
+ * Function: ml_menu_size
+ * Function: ml_menu_pack
+ *
+ * The monolith menu widget is part of monolith's support for
+ * pull-down menus. Menu widgets are normally created and packed
+ * into a menubar widget (see @ref{new_ml_menubar(3)}).
+ *
+ * Menus can contain different menu items:
+ *
+ * * Simple buttons. When picked by the user, these cause a callback
+ * function to be invoked.
+ *
+ * * Inactive buttons.
+ *
+ * * Separators. Used to separate groups of buttons.
+ *
+ * * Submenus. Nested menus to any depth.
+ *
+ * (In future we will add other types of menu item, in particular check
+ * boxes and radio buttons).
+ *
+ * To create a menu, call @code{new_ml_menu}. This makes a new, empty
+ * menu.
+ *
+ * To populate a menu, you can create menuitem objects of the required
+ * type and call @code{ml_menu_push_back}, @code{ml_menu_push_front},
+ * @code{ml_menu_insert} or @code{ml_menu_replace} as required. However,
+ * this class offers short-cut functions which do the job of creating
+ * the menu item and inserting it in the menu. These are described
+ * below:
+ *
+ * @code{ml_menu_push_back_button} creates a button menuitem and
+ * appends it to the menu. It is exactly equivalent to calling
+ * @code{new_ml_menuitem_button}, then @code{ml_menuitem_button_set_callback},
+ * then @code{ml_menu_push_back}.
+ *
+ * @code{ml_menu_push_back_inactive_button} creates an inactive button
+ * menuitem and appends it to the menu. It is exactly equivalent to calling
+ * @code{new_ml_menuitem_button} followed by @code{ml_menu_push_back}.
+ *
+ * @code{ml_menu_push_back_separator} creates a separator menuitem
+ * and appends it to the menu. It is exactly equivalent to calling
+ * @code{new_ml_menuitem_separator} followed by @code{ml_menu_push_back}.
+ *
+ * @code{ml_menu_pack} is equivalent to @code{ml_menu_push_back}.
+ *
+ * See also: @ref{new_ml_menubar(3)}, @ref{new_ml_menuitem_button(3)},
+ * @ref{new_ml_menuitem_separator(3)},
+ * @ref{vector_push_back(3)}, @ref{vector_pop_back(3)},
+ * @ref{vector_push_front(3)}, @ref{vector_pop_front(3)},
+ * @ref{vector_get(3)}, @ref{vector_insert(3)}, @ref{vector_replace(3)},
+ * @ref{vector_erase(3)}, @ref{vector_clear(3)}, @ref{vector_size(3)}.
+ */
+extern ml_menu new_ml_menu (pool pool);
+extern void ml_menu_push_back (ml_menu, ml_widget menu_or_menuitem);
+extern void ml_menu_push_back_button (ml_menu, const char *text, void (*fn) (ml_session, void *), ml_session session, void *data);
+extern void ml_menu_push_back_inactive_button (ml_menu, const char *text);
+extern void ml_menu_push_back_separator (ml_menu);
+extern ml_widget ml_menu_pop_back (ml_menu);
+extern void ml_menu_push_front (ml_menu, ml_widget menu_or_menuitem);
+extern ml_widget ml_menu_pop_front (ml_menu);
+extern ml_widget ml_menu_get (ml_menu, int i);
+extern void ml_menu_insert (ml_menu, int i, ml_widget menu_or_menuitem);
+extern void ml_menu_replace (ml_menu, int i, ml_widget menu_or_menuitem);
+extern void ml_menu_erase (ml_menu, int i);
+extern void ml_menu_clear (ml_menu);
+extern int ml_menu_size (ml_menu);
+extern void ml_menu_pack (ml_menu, ml_widget menu_or_menuitem);
+
+/* Function: new_ml_menuitem_button
+ * Function: ml_menuitem_button_set_callback
+ * Function: new_ml_menuitem_separator
+ *
+ * Monolith menuitem widgets are part of monolith's support for
+ * pull-down menus. Menuitem widgets are individual menu entries which
+ * are normally created and packed into menu widgets
+ * (see @ref{new_ml_menu(3)}).
+ *
+ * Menuitem buttons are active or inactive buttons which when clicked
+ * cause a callback function to be invoked. @code{new_ml_menuitem_button}
+ * creates a new button with the given text.
+ * @code{ml_menuitem_button_set_callback} sets the callback function
+ * on the button to @code{fn}. If the function is set to @code{NULL}, then
+ * the button becomes inactive (see also @ref{new_ml_button(3)}).
+ *
+ * Menuitem separators can be used to group logically similar sets of
+ * buttons. @code{new_ml_menuitem_separator} creates a new separator.
+ *
+ * See also: @ref{new_ml_menu(3)}, and the following convenience functions:
+ * @ref{ml_menu_push_back_button(3)},
+ * @ref{ml_menu_push_back_inactive_button(3)},
+ * @ref{ml_menu_push_back_separator(3)},
+ */
+extern ml_menuitem_button new_ml_menuitem_button (pool pool, const char *text);
+extern void ml_menuitem_button_set_callback (ml_menuitem_button, void (*fn) (ml_session, void *), ml_session session, void *data);
+extern ml_menuitem_separator new_ml_menuitem_separator (pool pool);
+
+#endif /* ML_MENU_H */
diff --git a/src/ml_multicol_layout.c b/src/ml_multicol_layout.c
new file mode 100644 (file)
index 0000000..1e10b2e
--- /dev/null
@@ -0,0 +1,149 @@
+/* Monolith multi-column layout class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_multicol_layout.c,v 1.3 2002/11/13 20:46:37 rich Exp $
+ */
+
+#include "config.h"
+
+#include <pthr_iolib.h>
+
+#include "ml_widget.h"
+#include "monolith.h"
+#include "ml_table_layout.h"
+#include "ml_multicol_layout.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property properties[];
+
+struct ml_widget_operations multicol_layout_ops =
+  {
+    repaint: repaint,
+    properties: properties,
+  };
+
+struct ml_multicol_layout
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_table_layout tbl;         /* We're really just a table layout. */
+  int cols;                    /* Fixed number of columns. */
+  int rows;                    /* Current number of rows in the table. */
+  int r, c;                    /* Next row, column. */
+  const char *clazz;           /* Stylesheet class. */
+};
+
+static void update_table_class (void *vw);
+
+static struct ml_widget_property properties[] =
+  {
+    { name: "class",
+      offset: ml_offsetof (struct ml_multicol_layout, clazz),
+      type: ML_PROP_STRING,
+      on_set: update_table_class },
+    { 0 }
+  };
+
+static void make_cell (ml_multicol_layout w);
+
+ml_multicol_layout
+new_ml_multicol_layout (pool pool, int nr_cols)
+{
+  ml_multicol_layout w = pmalloc (pool, sizeof *w);
+
+  w->ops = &multicol_layout_ops;
+  w->pool = pool;
+  w->tbl = new_ml_table_layout (pool, 0, nr_cols);
+  w->cols = nr_cols;
+  w->rows = 0;
+  w->r = w->c = 0;
+  w->clazz = 0;
+
+  return w;
+}
+
+static void
+update_table_class (void *vw)
+{
+  ml_multicol_layout w = (ml_multicol_layout) vw;
+
+  ml_widget_set_property (w->tbl, "class", w->clazz);
+}
+
+void
+ml_multicol_layout_set_align (ml_multicol_layout w, const char *align)
+{
+  make_cell (w);
+  ml_table_layout_set_align (w->tbl, w->r, w->c, align);
+}
+
+void
+ml_multicol_layout_set_valign (ml_multicol_layout w, const char *valign)
+{
+  make_cell (w);
+  ml_table_layout_set_valign (w->tbl, w->r, w->c, valign);
+}
+
+void
+ml_multicol_layout_set_class (ml_multicol_layout w, const char *clazz)
+{
+  make_cell (w);
+  ml_table_layout_set_class (w->tbl, w->r, w->c, clazz);
+}
+
+void
+ml_multicol_layout_set_header (ml_multicol_layout w, int is_header)
+{
+  make_cell (w);
+  ml_table_layout_set_header (w->tbl, w->r, w->c, is_header);
+}
+
+void
+ml_multicol_layout_pack (ml_multicol_layout w, ml_widget _w)
+{
+  make_cell (w);
+
+  /* Insert the widget. */
+  if (_w) ml_table_layout_pack (w->tbl, _w, w->r, w->c);
+
+  /* Move to the next position. */
+  w->c++;
+  if (w->c >= w->cols)
+    {
+      w->c = 0;
+      w->r++;
+    }
+}
+
+static void
+make_cell (ml_multicol_layout w)
+{
+  /* Create a new row in the underlying table? */
+  if (w->r >= w->rows)
+    {
+      ml_table_layout_add_row (w->tbl);
+      w->rows++;
+    }
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_multicol_layout w = (ml_multicol_layout) vw;
+
+  ml_widget_repaint (w->tbl, session, windowid, io);
+}
diff --git a/src/ml_multicol_layout.h b/src/ml_multicol_layout.h
new file mode 100644 (file)
index 0000000..18e3545
--- /dev/null
@@ -0,0 +1,76 @@
+/* Monolith multi-column layout class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_multicol_layout.h,v 1.3 2002/11/13 20:46:37 rich Exp $
+ */
+
+#ifndef ML_MULTICOL_LAYOUT_H
+#define ML_MULTICOL_LAYOUT_H
+
+#include <ml_widget.h>
+
+struct ml_multicol_layout;
+typedef struct ml_multicol_layout *ml_multicol_layout;
+
+/* Function: new_ml_multicol_layout - monolith multi-column layout widget
+ * Function: ml_multicol_layout_set_align
+ * Function: ml_multicol_layout_set_valign
+ * Function: ml_multicol_layout_set_class
+ * Function: ml_multicol_layout_set_header
+ * Function: ml_multicol_layout_pack
+ *
+ * The multi-column layout widget is a very simple type of layout widget
+ * suitable for forms and simple tabular data. The widget is initialised
+ * with the fixed number of columns, then each call to the pack method
+ * adds a widget at the next available free place. This layout widget
+ * creates more rows as required, unlike the table layout widget where
+ * rows must be created explicitly.
+ *
+ * Note: When creating a form, it is best to use the specialised version
+ * of multi-column layout, @code{ml_form_layout} (see
+ * @ref{new_ml_form_layout(3)}).
+ *
+ * @code{new_ml_multicol_layout} creates a new multi-column layout
+ * widget. The fixed number of columns is specified in the
+ * @code{nr_cols} parameter.
+ *
+ * The following properties can be changed on multi-column layouts (see
+ * @ref{ml_widget_set_property(3)}):
+ *
+ * @code{class}: The stylesheet class.
+ *
+ * @code{ml_multicol_layout_set_(valign|align|class|header)} sets the
+ * cell alignment, class or header flag for the NEXT cell (ie. you
+ * must call this function BEFORE calling the pack function). See
+ * @ref{ml_table_layout_set_valign(3)},
+ * @ref{ml_table_layout_set_align(3)},
+ * @ref{ml_table_layout_set_class(3)} and
+ * @ref{ml_table_layout_set_header(3)}.
+ *
+ * @code{ml_multicol_layout_pack} packs another widget at the next
+ * available free space, going left-to-right, top-to-bottom. An additional
+ * rows is created if required. To pack an empty cell, call this
+ * function with the widget parameter set to @code{NULL}.
+ */
+extern ml_multicol_layout new_ml_multicol_layout (pool pool, int nr_cols);
+extern void ml_multicol_layout_set_align (ml_multicol_layout, const char *align);
+extern void ml_multicol_layout_set_valign (ml_multicol_layout, const char *valign);
+extern void ml_multicol_layout_set_class (ml_multicol_layout, const char *clazz);
+extern void ml_multicol_layout_set_header (ml_multicol_layout, int is_header);
+extern void ml_multicol_layout_pack (ml_multicol_layout, ml_widget);
+
+#endif /* ML_MULTICOL_LAYOUT_H */
diff --git a/src/ml_select_layout.c b/src/ml_select_layout.c
new file mode 100644 (file)
index 0000000..f1fa342
--- /dev/null
@@ -0,0 +1,342 @@
+/* Monolith select layout class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_select_layout.c,v 1.3 2002/12/02 10:27:18 rich Exp $
+ */
+
+#include "config.h"
+
+#include <assert.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <vector.h>
+
+#include <pthr_iolib.h>
+
+#include "ml_widget.h"
+#include "monolith.h"
+#include "ml_smarttext.h"
+#include "ml_select_layout.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property properties[];
+
+struct ml_widget_operations select_layout_ops =
+  {
+    repaint: repaint,
+    properties: properties,
+  };
+
+struct ml_select_layout
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_session session;          /* Current session. */
+  vector tabs;                 /* Vector of tabs. */
+  int selected;                        /* Index of selected tab. */
+  const char *clazz;           /* Class of top-level table. */
+  ml_widget top;               /* "Top" widget (see manpage). */
+  ml_widget bottom;            /* "Bottom" widget (see manpage). */
+};
+
+struct tab
+{
+  const char *name;            /* Name of the tab. */
+  ml_widget w;                 /* Widget. */
+
+  /* Do not set this directly. Use make_action and del_action functions. */
+  const char *action_id;
+};
+
+static struct ml_widget_property properties[] =
+  {
+    { name: "class",
+      offset: ml_offsetof (struct ml_select_layout, clazz),
+      type: ML_PROP_STRING },
+    { name: "select_layout.top",
+      offset: ml_offsetof (struct ml_select_layout, top),
+      type: ML_PROP_WIDGET },
+    { name: "select_layout.bottom",
+      offset: ml_offsetof (struct ml_select_layout, bottom),
+      type: ML_PROP_WIDGET },
+    { 0 }
+  };
+
+ml_select_layout
+new_ml_select_layout (pool pool, ml_session session)
+{
+  ml_select_layout w = pmalloc (pool, sizeof *w);
+
+  w->ops = &select_layout_ops;
+  w->pool = pool;
+  w->session = session;
+  w->tabs = new_vector (pool, struct tab);
+  w->selected = 0;
+  w->clazz = 0;
+  w->top = w->bottom = 0;
+
+  return w;
+}
+
+static void make_action (ml_select_layout w, struct tab *tab);
+static void del_action (ml_select_layout w, struct tab *tab);
+
+void
+ml_select_layout_push_back (ml_select_layout w, const char *name, ml_widget _w)
+{
+  struct tab tab;
+
+  tab.name = name;
+  tab.w = _w;
+  make_action (w, &tab);
+  vector_push_back (w->tabs, tab);
+}
+
+ml_widget
+ml_select_layout_pop_back (ml_select_layout w)
+{
+  struct tab tab;
+
+  vector_pop_back (w->tabs, tab);
+  del_action (w, &tab);
+  return tab.w;
+}
+
+void
+ml_select_layout_push_front (ml_select_layout w,
+                            const char *name, ml_widget _w)
+{
+  struct tab tab;
+
+  tab.name = name;
+  tab.w = _w;
+  make_action (w, &tab);
+  vector_push_front (w->tabs, tab);
+}
+
+ml_widget
+ml_select_layout_pop_front (ml_select_layout w)
+{
+  struct tab tab;
+
+  vector_pop_front (w->tabs, tab);
+  del_action (w, &tab);
+  return tab.w;
+}
+
+ml_widget
+ml_select_layout_get (ml_select_layout w, int i)
+{
+  struct tab tab;
+
+  vector_get (w->tabs, i, tab);
+  return tab.w;
+}
+
+void
+ml_select_layout_insert (ml_select_layout w, int i,
+                        const char *name, ml_widget _w)
+{
+  struct tab tab;
+
+  tab.name = name;
+  tab.w = _w;
+  make_action (w, &tab);
+  vector_insert (w->tabs, i, tab);
+}
+
+void
+ml_select_layout_replace (ml_select_layout w, int i,
+                         const char *name, ml_widget _w)
+{
+  struct tab tab;
+
+  vector_get (w->tabs, i, tab);
+  del_action (w, &tab);
+
+  tab.name = name;
+  tab.w = _w;
+  make_action (w, &tab);
+  vector_replace (w->tabs, i, tab);
+}
+
+void
+ml_select_layout_erase (ml_select_layout w, int i)
+{
+  struct tab tab;
+
+  vector_get (w->tabs, i, tab);
+  del_action (w, &tab);
+
+  vector_erase (w->tabs, i);
+}
+
+void
+ml_select_layout_clear (ml_select_layout w)
+{
+  int i;
+
+  for (i = 0; i < vector_size (w->tabs); ++i)
+    {
+      struct tab tab;
+
+      vector_get (w->tabs, i, tab);
+      del_action (w, &tab);
+    }
+
+  vector_clear (w->tabs);
+}
+
+int
+ml_select_layout_size (ml_select_layout w)
+{
+  return vector_size (w->tabs);
+}
+
+void
+ml_select_layout_pack (ml_select_layout w, const char *name, ml_widget _w)
+{
+  ml_select_layout_push_back (w, name, _w);
+}
+
+void
+ml_select_layout_set_selection (ml_select_layout w, int i)
+{
+  w->selected = i;
+}
+
+int
+ml_select_layout_get_selection (ml_select_layout w)
+{
+  return w->selected;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_select_layout w = (ml_select_layout) vw;
+  int i;
+  struct tab *tab;
+  const char *clazz = w->clazz ? : "ml_select_layout";
+
+  /* Verify the selection is in range. If not, bring it into range. */
+  if (w->selected < 0) w->selected = 0;
+  else if (w->selected >= vector_size (w->tabs))
+    w->selected = vector_size (w->tabs) - 1;
+
+  /* Begin the table. */
+  io_fprintf (io, "<table class=\"%s\"><tr><td valign=\"top\">", clazz);
+
+  /* Left hand column. */
+  if (w->top) ml_widget_repaint (w->top, session, windowid, io);
+
+  io_fputs ("<table class=\"ml_select_layout_left\">", io);
+
+  for (i = 0; i < vector_size (w->tabs); ++i)
+    {
+      vector_get_ptr (w->tabs, i, tab);
+
+      io_fputs ("<tr>", io);
+      if (i == w->selected) io_fputs ("<th>", io);
+      else io_fputs ("<td>", io);
+
+      io_fprintf (io, "<a href=\"%s?ml_action=%s&ml_window=%s\">",
+                 ml_session_script_name (w->session),
+                 tab->action_id, windowid);
+      ml_plaintext_print (io, tab->name);
+      io_fputs ("</a>", io);
+
+      if (i == w->selected) io_fputs ("</th>", io);
+      else io_fputs ("</td>", io);
+      io_fputs ("</tr>\n", io);
+    }
+
+  io_fputs ("</table>", io);
+
+  if (w->bottom) ml_widget_repaint (w->bottom, session, windowid, io);
+
+  io_fputs ("</td>\n<td valign=\"top\">", io);
+
+  /* Right hand column: the widget. */
+  vector_get_ptr (w->tabs, w->selected, tab);
+
+  if (tab->w) ml_widget_repaint (tab->w, session, windowid, io);
+
+  io_fputs ("</td></tr></table>", io);
+}
+
+static void do_select (ml_session session, void *vargs);
+
+struct select_args
+{
+  ml_select_layout w;          /* The widget. */
+  const char *action_id;       /* The action ID. */
+};
+
+static void
+make_action (ml_select_layout w, struct tab *tab)
+{
+  struct select_args *args = pmalloc (w->pool, sizeof *args);
+
+  args->w = w;
+  args->action_id = tab->action_id =
+    ml_register_action (w->session, do_select, args);
+}
+
+static void
+del_action (ml_select_layout w, struct tab *tab)
+{
+  ml_unregister_action (w->session, tab->action_id);
+}
+
+/* This function is called when the user clicks on one of the items on the
+ * left hand side.
+ *
+ * Because items can move around, we didn't store the absolute item number
+ * in the struct select_args. Instead, we stored the action_id, which we'll
+ * use to search through the current tabs to find out which tab the
+ * user was trying to click.
+ */
+static void
+do_select (ml_session session, void *vargs)
+{
+  struct select_args *args = (struct select_args *) vargs;
+  const char *action_id = args->action_id;
+  ml_select_layout w = args->w;
+  int i;
+  struct tab *tab;
+
+  for (i = 0; i < vector_size (w->tabs); ++i)
+    {
+      vector_get_ptr (w->tabs, i, tab);
+
+      if (strcmp (tab->action_id, action_id) == 0)
+       {
+         /* Found it. */
+         w->selected = i;
+         return;
+       }
+    }
+
+  /* Don't worry if we don't find it. Perhaps the underlying code has
+   * deleted this item, but the browser window was not updated.
+   */
+}
diff --git a/src/ml_select_layout.h b/src/ml_select_layout.h
new file mode 100644 (file)
index 0000000..c5a9518
--- /dev/null
@@ -0,0 +1,103 @@
+/* Monolith select layout class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_select_layout.h,v 1.2 2002/11/29 10:43:03 rich Exp $
+ */
+
+#ifndef ML_SELECT_LAYOUT_H
+#define ML_SELECT_LAYOUT_H
+
+#include <ml_widget.h>
+
+struct ml_select_layout;
+typedef struct ml_select_layout *ml_select_layout;
+
+/* Function: new_ml_select_layout - monolith select layout widget
+ * Function: ml_select_layout_push_back
+ * Function: ml_select_layout_pop_back
+ * Function: ml_select_layout_push_front
+ * Function: ml_select_layout_pop_front
+ * Function: ml_select_layout_get
+ * Function: ml_select_layout_insert
+ * Function: ml_select_layout_replace
+ * Function: ml_select_layout_erase
+ * Function: ml_select_layout_clear
+ * Function: ml_select_layout_size
+ * Function: ml_select_layout_pack
+ * Function: ml_select_layout_set_selection
+ * Function: ml_select_layout_get_selection
+ *
+ * This is the select layout widget. The appearance of select layouts
+ * is similar to the "preferences" dialog in versions of the Netscape
+ * browser. On the left is a list of items to choose from. On the right,
+ * the chosen item is displayed. The user may click an item from the
+ * list on the left in order to change the widget which is shown on the
+ * right.
+ *
+ * @code{new_ml_select_layout} creates a new, empty select layout widget.
+ *
+ * The following properties can be changed on select layout widgets (see
+ * @ref{ml_widget_set_property(3)}):
+ *
+ * @code{class}: The stylesheet class.
+ *
+ * @code{select_layout.top} (a widget): A widget displayed above
+ * the list of selections (the default is @code{NULL} meaning nothing
+ * is displayed there).
+ *
+ * @code{select_layout.bottom} (a widget): A widget displayed below
+ * the list of selections (the default is @code{NULL} meaning nothing
+ * is displayed there).
+ *
+ * Underlying the select layout widget is a simple c2lib vector, and the
+ * other access functions use the same notation as the equivalent
+ * c2lib @code{vector_*} functions. Go to the SEE ALSO section
+ * below to see how to manipulate widgets within a flow layout.
+ *
+ * @code{ml_select_layout_pack} is equivalent to
+ * @code{ml_select_layout_push_back}: it appends the item to the
+ * end of the current vector of widgets.
+ *
+ * @code{ml_select_layout_set_selection} sets the currently selected
+ * widget. This highlights the selection name on the left and displays
+ * the corresponding widget on the right. The default is that the first
+ * (index 0) widget is displayed.
+ *
+ * @code{ml_select_layout_get_selection} returns the currently selected
+ * widget.
+ *
+ * See also: @ref{vector_push_back(3)}, @ref{vector_pop_back(3)},
+ * @ref{vector_push_front(3)}, @ref{vector_pop_front(3)},
+ * @ref{vector_get(3)}, @ref{vector_insert(3)}, @ref{vector_replace(3)},
+ * @ref{vector_erase(3)}, @ref{vector_clear(3)}, @ref{vector_size(3)}.
+ */
+extern ml_select_layout new_ml_select_layout (pool pool, ml_session session);
+extern void ml_select_layout_push_back (ml_select_layout, const char *name, ml_widget);
+extern ml_widget ml_select_layout_pop_back (ml_select_layout);
+extern void ml_select_layout_push_front (ml_select_layout, const char *name, ml_widget);
+extern ml_widget ml_select_layout_pop_front (ml_select_layout);
+extern ml_widget ml_select_layout_get (ml_select_layout, int i);
+extern void ml_select_layout_insert (ml_select_layout, int i, const char *name, ml_widget);
+extern void ml_select_layout_replace (ml_select_layout, int i, const char *name, ml_widget);
+extern void ml_select_layout_erase (ml_select_layout, int i);
+extern void ml_select_layout_clear (ml_select_layout);
+extern int ml_select_layout_size (ml_select_layout);
+extern void ml_select_layout_pack (ml_select_layout, const char *name, ml_widget);
+extern void ml_select_layout_set_selection (ml_select_layout, int i);
+extern int ml_select_layout_get_selection (ml_select_layout);
+
+#endif /* ML_SELECT_LAYOUT_H */
diff --git a/src/ml_smarttext.h b/src/ml_smarttext.h
new file mode 100644 (file)
index 0000000..0a8170c
--- /dev/null
@@ -0,0 +1,102 @@
+/* Monolith smart text library.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_smarttext.h,v 1.1 2002/12/08 17:29:17 rich Exp $
+ */
+
+#ifndef ML_SMARTTEXT_H
+#define ML_SMARTTEXT_H
+
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+
+/* Function: ml_plaintext_print - convert text to HTML
+ * Function: ml_plaintext_to_html
+ * Function: ml_smarttext_print
+ * Function: ml_smarttext_to_html
+ * Function: ml_filterhtml_print
+ * Function: ml_filterhtml_to_html
+ * Function: ml_anytext_print
+ * Function: ml_anytext_to_html
+ *
+ * The monolith "smart text" library is concerned with rendering
+ * plain text, smart text and filtered HTML safely in the browser.
+ *
+ * Plain text is just ordinary text, in which we escape the HTML
+ * special entities such as @code{<} and @code{>} so that the user
+ * cannot put up arbitrary HTML.
+ *
+ * Smart text is ordinary text with user-friendly markup sequences
+ * (see below) allow the user to easily make text bold, etc.
+ *
+ * Filtered HTML is a limited, safe subset of HTML.
+ *
+ * The library currently supports the following smart text
+ * constructs:
+ *
+ * - URLs.
+ *
+ * - @code{mailto:} URLs are marked up with a special mail icon.
+ *
+ * - @code{*bold*}, @code{/italic/} and @code{=monospace=} text, but
+ * only around simple text, and currently you cannot combine these
+ * with other smart text markup inside the text.
+ *
+ * - Various smileys, including @code{:-)}, @code{:-(} and @code{:-P}.
+ *
+ * - @code{(C)}, @code{(R)}, @code{(TM)} are marked up as the appropriate
+ * symbol.
+ *
+ * - @code{1/4}, @code{1/2}, @code{3/4} are marked up as fractions.
+ *
+ * @code{ml_plaintext_print} converts a string @code{text} containing
+ * just plain text to HTML and writes it directly to @code{io}.
+ *
+ * @code{ml_plaintext_to_html} converts a string @code{text} containing
+ * just plain text to HTML and returns this as a new string allocated
+ * in @code{pool}.
+ *
+ * @code{ml_smarttext_print} converts a string @code{text} containing
+ * smart text to HTML and writes it directly to @code{io}.
+ *
+ * @code{ml_smarttext_to_html} converts a string @code{text} containing
+ * smart text to HTML and returns this as a new string allocated
+ * in @code{pool}.
+ *
+ * @code{ml_filterhtml_print} converts a string @code{text} containing
+ * filtered HTML to HTML and writes it directly to @code{io}.
+ *
+ * @code{ml_filterhtml_to_html} converts a string @code{text} containing
+ * filtered HTML to HTML and returns this as a new string allocated
+ * in @code{pool}.
+ *
+ * @code{ml_anytext_print} and @code{ml_anytext_to_html} can be used
+ * on either plain text, smart text or filtered HTML, depending on the
+ * value passed in the @code{type} argument, which must be one of
+ * @code{'p'}, @code{'s'} or @code{'h'}.
+ */
+extern void ml_plaintext_print (io_handle io, const char *text);
+extern const char *ml_plaintext_to_html (pool, const char *text);
+extern void ml_smarttext_print (io_handle io, const char *text);
+extern const char *ml_smarttext_to_html (pool, const char *text);
+extern void ml_filterhtml_print (io_handle io, const char *text);
+extern const char *ml_filterhtml_to_html (pool, const char *text);
+extern void ml_anytext_print (io_handle io, const char *text, char type);
+extern const char *ml_anytext_to_html (pool, const char *text, char type);
+
+#endif /* ML_SMARTTEXT_H */
diff --git a/src/ml_smarttext.l b/src/ml_smarttext.l
new file mode 100644 (file)
index 0000000..f3ec9ed
--- /dev/null
@@ -0,0 +1,178 @@
+/* Monolith smart text. -*- C -*-
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_smarttext.l,v 1.1 2002/12/08 17:29:17 rich Exp $
+ */
+
+%{
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+
+#include "monolith.h"
+#include "ml_smarttext.h"
+
+/* This removes a warning message. */
+#define YY_NO_UNPUT 1
+
+static void emit_str (const char *s);
+static void emit_strn (const char *str, const char *end);
+static void emit_char (char c);
+%}
+
+%option noyywrap
+
+       /* Characters in URLs. */
+URLCHAR [^ \t\)<>]
+
+       /* Characters in bold/italic/monospace text. */
+ORDCHARS [[:alnum:] \t,\']
+
+%%
+
+       /* URLs. */
+[[:lower:]]+"://"{URLCHAR}+ {
+  emit_str ("<a href=\"");
+  emit_str (yytext);
+  emit_str ("\" target=\"_new\">");
+  emit_str (yytext);
+  emit_str ("</a>");
+}
+
+       /* Special mailto: URLs. */
+       /* XXX Email addresses. */
+mailto:{URLCHAR}+ {
+  emit_str ("<a href=\"");
+  emit_str (yytext);
+  emit_str ("\" target=\"_new\"><img src=\"/ml-icons/envelope.gif\">&nbsp;");
+  emit_str (yytext+7);
+  emit_str ("</a>");
+}
+
+       /* Fractions - must appear before italics. */
+"1/4"  emit_str ("&frac14;");
+"1/2"  emit_str ("&frac12;");
+"3/4"  emit_str ("&frac34;");
+
+       /* Special characters. */
+"(C)"  emit_str ("&copy;");
+"(R)"  emit_str ("&reg;");
+"(TM)" emit_str ("<sup>TM</sup>");
+
+       /* Bold, italic, monospace text. */
+"*"{ORDCHARS}+"*" {
+  emit_str ("<b>");
+  emit_strn (yytext+1, &yytext[strlen (yytext)-1]);
+  emit_str ("</b>");
+}
+"/"{ORDCHARS}+"/" {
+  emit_str ("<i>");
+  emit_strn (yytext+1, &yytext[strlen (yytext)-1]);
+  emit_str ("</i>");
+}
+"="{ORDCHARS}+"=" {
+  emit_str ("<code>");
+  emit_strn (yytext+1, &yytext[strlen (yytext)-1]);
+  emit_str ("</code>");
+}
+
+       /* Smileys. */
+[:;][-oO][\)>] {
+  emit_str ("<img src=\"/ml-icons/smiley.gif\">");
+}
+[:;][-oO][\(<] {
+  emit_str ("<img src=\"/ml-icons/sad.gif\">");
+}
+[:;][-oO][pP] {
+  emit_str ("<img src=\"/ml-icons/tongue.gif\">");
+}
+
+       /* HTML entities which need to be escaped. */
+"&"    emit_str ("&amp;");
+"<"    emit_str ("&lt;");
+">"    emit_str ("&gt;");
+\"     emit_str ("&quot;");
+\n     emit_str ("<br>");
+
+       /* Anything else just gets emitted as a normal character. */
+.      emit_char (yytext[0]);
+
+%%
+
+/* These global variables control where the output gets sent.
+ * Note that because none of this code blocks, we don't need to
+ * worry about these globals getting corrupted.
+ */
+static pool out_pool;          /* Pool for allocations. */
+static char *out_str;          /* Output string. */
+static int used, allocated;    /* Space used and allocated in the string. */
+
+const char *
+ml_smarttext_to_html (pool pool, const char *text)
+{
+  YY_BUFFER_STATE buf;
+
+  /* Set up the global variables so that output goes to our string. */
+  out_pool = pool;
+  out_str = 0;
+  used = allocated = 0;
+
+  /* We're going to scan from this buffer (instead of stdin). */
+  buf = yy_scan_string (text);
+
+  /* Run the scanner. */
+  ml_smarttext_lex ();
+
+  /* Clean up. */
+  yy_delete_buffer (buf);
+
+  /* Tack an ASCII NUL on the end of the string to terminate it. */
+  emit_char ('\0');
+
+  return out_str;
+}
+
+static void
+emit_str (const char *s)
+{
+  while (*s) emit_char (*s++);
+}
+
+static void
+emit_strn (const char *str, const char *end)
+{
+  while (str < end) emit_char (*str++);
+}
+
+static void
+emit_char (char c)
+{
+  if (used >= allocated)
+    {
+      allocated += 32;
+      out_str = prealloc (out_pool, out_str, allocated);
+    }
+
+  out_str[used++] = c;
+}
diff --git a/src/ml_tabbed_layout.c b/src/ml_tabbed_layout.c
new file mode 100644 (file)
index 0000000..8ccb160
--- /dev/null
@@ -0,0 +1,301 @@
+/* Monolith tabbed layout class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_tabbed_layout.c,v 1.1 2002/11/13 20:46:37 rich Exp $
+ */
+
+#include "config.h"
+
+#include <assert.h>
+
+#include <pool.h>
+#include <vector.h>
+
+#include <pthr_iolib.h>
+
+#include "ml_widget.h"
+#include "monolith.h"
+#include "ml_tabbed_layout.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations tabbed_layout_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_tabbed_layout
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  vector tabs;                 /* Vector of tabs. */
+  vector order;                        /* Display order (first = top). */
+};
+
+struct tab
+{
+  const char *name;            /* Name of the tab. */
+  ml_widget w;                 /* Widget. */
+}
+
+ml_tabbed_layout
+new_ml_tabbed_layout (pool pool)
+{
+  ml_tabbed_layout w = pmalloc (pool, sizeof *w);
+
+  w->ops = &tabbed_layout_ops;
+  w->pool = pool;
+  w->tabs = new_vector (pool, struct tab);
+  w->order = new_vector (pool, int);
+
+  return w;
+}
+
+static inline void
+add_tab_to_order (ml_tabbed_layout w, int index)
+{
+  vector_push_back (w->order, index);
+}
+
+static inline void
+del_tab_from_order (ml_tabbed_layout w, int index)
+{
+  int n, i;
+
+  for (n = 0; n < vector_size (w->order); ++n)
+    {
+      vector_get (w->order, n, i);
+      if (i == index)
+       {
+         vector_erase (w->order, n);
+         break;
+       }
+    }
+}
+
+static inline void
+increment_order (ml_tabbed_layout w, int first_index)
+{
+  int n, i;
+
+  for (n = 0; n < vector_size (w->order); ++n)
+    {
+      vector_get (w->order, n, i);
+      if (i >= first_index)
+       {
+         i++;
+         vector_replace (w->order, n, i);
+       }
+    }
+}
+
+static inline void
+decrement_order (ml_tabbed_layout w, int first_index)
+{
+  int n, i;
+
+  for (n = 0; n < vector_size (w->order); ++n)
+    {
+      vector_get (w->order, n, i);
+      if (i >= first_index)
+       {
+         i++;
+         vector_replace (w->order, n, i);
+       }
+    }
+}
+
+void
+ml_tabbed_layout_push_back (ml_tabbed_layout w, const char *name, ml_widget _w)
+{
+  struct tab tab;
+
+  tab.name = name;
+  tab.w = _w;
+  vector_push_back (w->tabs, tab);
+
+  add_tab_to_order (w, vector_size (w->tabs) - 1);
+}
+
+ml_widget
+ml_tabbed_layout_pop_back (ml_tabbed_layout w)
+{
+  struct tab tab;
+
+  vector_pop_back (w->tabs, tab);
+  del_tab_from_order (w, vector_size (w->tabs));
+  return tab.w;
+}
+
+void
+ml_tabbed_layout_push_front (ml_tabbed_layout w,
+                            const char *name, ml_widget _w)
+{
+  struct tab tab;
+
+  tab.name = name;
+  tab.w = _w;
+  vector_push_front (w->tabs, tab);
+
+  increment_order (w, 0);
+  add_tab_to_order (w, 0);
+}
+
+ml_widget
+ml_tabbed_layout_pop_front (ml_tabbed_layout w)
+{
+  struct tab tab;
+
+  vector_pop_front (w->tabs, tab);
+  del_tab_from_order (w, vector_size (w->tabs));
+  decrement_order (w, 1);
+  return tab.w;
+}
+
+ml_widget
+ml_tabbed_layout_get (ml_tabbed_layout w, int i)
+{
+  struct tab tab;
+
+  vector_get (w->tabs, i, tab);
+  return tab.w;
+}
+
+void
+ml_tabbed_layout_insert (ml_tabbed_layout w, int i,
+                        const char *name, ml_widget _w)
+{
+  struct tab tab;
+
+  tab.name = name;
+  tab.w = _w;
+  vector_insert (w->tabs, i, tab);
+
+  increment_order (w, i);
+  add_tab_to_order (w, i);
+}
+
+void
+ml_tabbed_layout_replace (ml_tabbed_layout w, int i,
+                         const char *name, ml_widget _w)
+{
+  struct tab tab;
+
+  tab.name = name;
+  tab.w = _w;
+  vector_replace (w->tabs, i, tab);
+
+  ml_tabbed_layout_send_to_back (w, i);
+}
+
+void
+ml_tabbed_layout_erase (ml_tabbed_layout w, int i)
+{
+  vector_erase (w->tabs, i);
+  del_tab_from_order (w, i);
+  decrement_order (w, i+1);
+}
+
+void
+ml_tabbed_layout_clear (ml_tabbed_layout w)
+{
+  vector_clear (w->tabs);
+  vector_clear (w->order);
+}
+
+int
+ml_tabbed_layout_size (ml_tabbed_layout w)
+{
+  return vector_size (w->tabs);
+}
+
+void
+ml_tabbed_layout_pack (ml_tabbed_layout w, const char *name, ml_widget _w)
+{
+  ml_tabbed_layout_push_back (w, name, _w);
+}
+
+void
+ml_tabbed_layout_bring_to_front (ml_tabbed_layout w, int i)
+{
+  int i;
+
+  vector_pop_back (w->order, i);
+  vector_push_front (w->order, i);
+}
+
+void
+ml_tabbed_layout_send_to_back (ml_tabbed_layout w, int i)
+{
+  int i;
+
+  vector_pop_front (w->order, i);
+  vector_push_back (w->order, i);
+}
+
+int
+ml_tabbed_layout_get_front_tab (ml_tabbed_layout w)
+{
+  int i;
+
+  vector_get (w->order, 0, i);
+  return i;
+}
+
+/* This function verifies the integrity of the internal structures. We
+ * can remove it once the tabbed layout has become a bit more mature.
+ */
+static inline void
+verify_structures (ml_tabbed_layout w)
+{
+  vector marks;
+  unsigned char c = 0;
+  int i, n;
+
+  assert (vector_size (w->tabs) == vector_size (w->order));
+
+  marks = new_vector (w->pool, unsigned char);
+  vector_fill (marks, c, vector_size (w->tabs));
+
+  for (n = 0; n < vector_size (w->order); ++n)
+    {
+      vector_get (w->order, n, i);
+      vector_get (marks, i, c);
+      assert (c == 0);
+      c = 1;
+      vector_replace (marks, i, c);
+    }
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_tabbed_layout w = (ml_tabbed_layout) vw;
+  int i;
+
+  verify_structures (w);
+
+  XXX;
+
+  for (i = 0; i < vector_size (w->v); ++i)
+    {
+      ml_widget _w;
+
+      vector_get (w->v, i, _w);
+      ml_widget_repaint (_w, session, windowid, io);
+    }
+}
diff --git a/src/ml_tabbed_layout.h b/src/ml_tabbed_layout.h
new file mode 100644 (file)
index 0000000..711616e
--- /dev/null
@@ -0,0 +1,110 @@
+/* Monolith tabbed layout class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_tabbed_layout.h,v 1.1 2002/11/13 20:46:38 rich Exp $
+ */
+
+#ifndef ML_TABBED_LAYOUT_H
+#define ML_TABBED_LAYOUT_H
+
+#include <ml_widget.h>
+
+struct ml_tabbed_layout;
+typedef struct ml_tabbed_layout *ml_tabbed_layout;
+
+/* Function: new_ml_tabbed_layout - monolith tabbed layout ("dialog") widget
+ * Function: ml_tabbed_layout_push_back
+ * Function: ml_tabbed_layout_pop_back
+ * Function: ml_tabbed_layout_push_front
+ * Function: ml_tabbed_layout_pop_front
+ * Function: ml_tabbed_layout_get
+ * Function: ml_tabbed_layout_insert
+ * Function: ml_tabbed_layout_replace
+ * Function: ml_tabbed_layout_erase
+ * Function: ml_tabbed_layout_clear
+ * Function: ml_tabbed_layout_size
+ * Function: ml_tabbed_layout_pack
+ * Function: ml_tabbed_layout_bring_to_front
+ * Function: ml_tabbed_layout_send_to_back
+ * Function: ml_tabbed_layout_get_front_tab
+ *
+ * This is the tabbed layout widget (often called a "tabbed dialog"). It
+ * displays a row of tabs along the top, allowing the user to select one
+ * of several widgets in the main area. The tabs have a three-dimensional
+ * appearance, giving the effect of multiple layers of widgets, similar
+ * to a card index.
+ *
+ * The monolith tabbed layout widget has some intentional limitations.
+ * First, and foremost, it only supports a small number of tabs. This
+ * is for good usability reasons (see, for instance, this page:
+ * @code{http://www.iarchitect.com/tabs.htm} ). If you want to have
+ * a widget with a large number of tabs, then a better approach is
+ * to use a tree which selects a widget (refer to the classic
+ * Netscape Navigator preferences window for an example).
+ * Secondly, the tabs may only appear at the top of the page.
+ *
+ * @code{new_ml_tabbed_layout} creates a new, empty tabbed layout widget.
+ *
+ * Underlying the tabbed layout is a simple c2lib vector, and the
+ * main access functions uses a similar notation to the equivalent
+ * c2lib @code{vector_*} functions. The exception is that each tab
+ * also has a @code{name} field, which is what is displayed on the
+ * tab itself. Go to the SEE ALSO section below
+ * to see how to manipulate widgets within the tabbed layout.
+ *
+ * @code{ml_tabbed_layout_pack} is equivalent to
+ * @code{ml_tabbed_layout_push_back}: it appends the widget to the end
+ * of the current vector of widgets.
+ *
+ * Any tab added is always added at the bottom of the pile.
+ *
+ * In left-to-right locales, the tabs appear in the same order that
+ * they are pushed into the vector.
+ *
+ * There are two further operations which can be carried out on
+ * tabs. The @code{ml_tabbed_layout_bring_to_front} function
+ * takes a tab index (ie. leftmost tab is 0, etc.) and brings that
+ * tab to the front or top of the pile. The
+ * @code{ml_tabbed_layout_send_to_back} function takes a tab
+ * index and sends that tab to the back or bottom of the pile.
+ * The user may also bring tabs to the front by clicking on them.
+ * @code{ml_tabbed_layout_get_front_tab} returns the index of
+ * the tab which is currently on top (hence of the widget which
+ * is currently displayed, since all other widgets will be hidden).
+ *
+ * See also: @ref{vector_push_back(3)}, @ref{vector_pop_back(3)},
+ * @ref{vector_push_front(3)}, @ref{vector_pop_front(3)},
+ * @ref{vector_get(3)}, @ref{vector_insert(3)}, @ref{vector_replace(3)},
+ * @ref{vector_erase(3)}, @ref{vector_clear(3)}, @ref{vector_size(3)}.
+ */
+extern ml_tabbed_layout new_ml_tabbed_layout (pool pool);
+extern void ml_tabbed_layout_push_back (ml_tabbed_layout, const char *name, ml_widget);
+extern ml_widget ml_tabbed_layout_pop_back (ml_tabbed_layout);
+extern void ml_tabbed_layout_push_front (ml_tabbed_layout, const char *name, ml_widget);
+extern ml_widget ml_tabbed_layout_pop_front (ml_tabbed_layout);
+extern ml_widget ml_tabbed_layout_get (ml_tabbed_layout, int i);
+extern void ml_tabbed_layout_insert (ml_tabbed_layout, int i, const char *name, ml_widget);
+extern void ml_tabbed_layout_replace (ml_tabbed_layout, int i, const char *name, ml_widget);
+extern void ml_tabbed_layout_erase (ml_tabbed_layout, int i);
+extern void ml_tabbed_layout_clear (ml_tabbed_layout);
+extern int ml_tabbed_layout_size (ml_tabbed_layout);
+extern void ml_tabbed_layout_pack (ml_tabbed_layout, const char *name, ml_widget);
+extern void ml_tabbed_layout_bring_to_front (ml_tabbed_layout, int i);
+extern void ml_tabbed_layout_send_to_back (ml_tabbed_layout, int i);
+extern int ml_tabbed_layout_get_front_tab (ml_tabbed_layout);
+
+#endif /* ML_TABBED_LAYOUT_H */
diff --git a/src/ml_table_layout.c b/src/ml_table_layout.c
new file mode 100644 (file)
index 0000000..5f96e17
--- /dev/null
@@ -0,0 +1,255 @@
+/* Monolith table layout class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_table_layout.c,v 1.3 2002/11/13 20:46:38 rich Exp $
+ */
+
+#include "config.h"
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#include <pthr_iolib.h>
+
+#include "ml_widget.h"
+#include "monolith.h"
+#include "ml_table_layout.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property properties[];
+
+struct ml_widget_operations table_layout_ops =
+  {
+    repaint: repaint,
+    properties: properties
+  };
+
+struct cell
+{
+  ml_widget w;                 /* Widget stored in the cell. */
+  const char *clazz;           /* Stylesheet class. */
+  unsigned char rowspan;       /* Rowspan. */
+  unsigned char colspan;       /* Colspan. */
+  unsigned char flags;         /* Various flags. */
+#define CELL_FLAGS_NO_PAINT  0x01
+#define CELL_FLAGS_IS_HEADER 0x02
+  char align;                  /* l|r|c */
+  char valign;                 /* t|b|m */
+};
+
+struct ml_table_layout
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  int rows, cols;              /* Number of rows, columns. */
+  const char *clazz;           /* Stylesheet class for the whole table. */
+
+  /* Cells are stored in this 2D array. */
+  struct cell **cells;
+};
+
+static struct ml_widget_property properties[] =
+  {
+    { name: "class",
+      offset: ml_offsetof (struct ml_table_layout, clazz),
+      type: ML_PROP_STRING },
+    { 0 }
+  };
+
+static inline struct cell *
+get_cell (ml_table_layout w, int row, int col)
+{
+  assert (0 <= row && row < w->rows);
+  assert (0 <= col && col < w->cols);
+  return &w->cells[row][col];
+}
+
+static inline void
+init_cell (ml_table_layout w, int r, int c)
+{
+  w->cells[r][c].w = 0;
+  w->cells[r][c].clazz = 0;
+  w->cells[r][c].rowspan = 1;
+  w->cells[r][c].colspan = 1;
+  w->cells[r][c].flags = 0;
+  w->cells[r][c].align = 'l';
+  w->cells[r][c].valign = 'm';
+}
+
+ml_table_layout
+new_ml_table_layout (pool pool, int rows, int cols)
+{
+  ml_table_layout w = pmalloc (pool, sizeof *w);
+  int r, c;
+
+  w->ops = &table_layout_ops;
+  w->pool = pool;
+  w->rows = rows;
+  w->cols = cols;
+  w->clazz = 0;
+
+  w->cells = pmalloc (pool, sizeof (struct cell *) * rows);
+  for (r = 0; r < rows; ++r)
+    {
+      w->cells[r] = pmalloc (pool, sizeof (struct cell) * cols);
+      for (c = 0; c < cols; ++c)
+       init_cell (w, r, c);
+    }
+
+  return w;
+}
+
+void
+ml_table_layout_pack (ml_table_layout w, ml_widget _w, int row, int col)
+{
+  get_cell (w, row, col)->w = _w;
+}
+
+void
+ml_table_layout_add_row (ml_table_layout w)
+{
+  int c;
+
+  w->cells = prealloc (w->pool,
+                      w->cells, sizeof (struct cell *) * (w->rows+1));
+  w->cells[w->rows] = pmalloc (w->pool, sizeof (struct cell) * w->cols);
+
+  for (c = 0; c < w->cols; ++c)
+    init_cell (w, w->rows, c);
+
+  w->rows++;
+}
+
+void
+ml_table_layout_set_colspan (ml_table_layout w, int row, int col, int colspan)
+{
+  assert (colspan > 0);
+  assert (col + colspan <= w->cols);
+  get_cell (w, row, col)->colspan = colspan;
+}
+
+void
+ml_table_layout_set_rowspan (ml_table_layout w, int row, int col, int rowspan)
+{
+  assert (rowspan > 0);
+  assert (row + rowspan <= w->rows);
+  get_cell (w, row, col)->rowspan = rowspan;
+}
+
+void
+ml_table_layout_set_align (ml_table_layout w, int row, int col,
+                          const char *align)
+{
+  get_cell (w, row, col)->align = align[0];
+}
+
+void
+ml_table_layout_set_valign (ml_table_layout w, int row, int col,
+                           const char *valign)
+{
+  get_cell (w, row, col)->valign = valign[0];
+}
+
+void
+ml_table_layout_set_class (ml_table_layout w, int row, int col,
+                          const char *clazz)
+{
+  get_cell (w, row, col)->clazz = clazz;
+}
+
+void
+ml_table_layout_set_header (ml_table_layout w, int row, int col,
+                           int is_header)
+{
+  if (is_header)
+    get_cell (w, row, col)->flags |= CELL_FLAGS_IS_HEADER;
+  else
+    get_cell (w, row, col)->flags &= ~CELL_FLAGS_IS_HEADER;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_table_layout w = (ml_table_layout) vw;
+  int r, c;
+
+  /* Clear all the NO_PAINT flags. */
+  for (r = 0; r < w->rows; ++r)
+    for (c = 0; c < w->cols; ++c)
+      get_cell (w, r, c)->flags &= ~CELL_FLAGS_NO_PAINT;
+
+  /* Start of the table. */
+  io_fputs ("<table", io);
+  if (w->clazz) io_fprintf (io, " class=\"%s\"", w->clazz);
+  io_fputs (">", io);
+
+  /* Paint the cells. */
+  for (r = 0; r < w->rows; ++r)
+    {
+      io_fprintf (io, "<tr>");
+
+      for (c = 0; c < w->cols; ++c)
+       {
+         struct cell *cl = get_cell (w, r, c);
+
+         if (!(cl->flags & CELL_FLAGS_NO_PAINT))
+           {
+             int i, j;
+
+             /* If there is a row or column span > 1, then we need to mark
+              * the cells in the "shadow" as not painted.
+              */
+             for (i = 0; i < cl->rowspan; ++i)
+               for (j = 0; j < cl->colspan; ++j)
+                 get_cell (w, r+i, c+j)->flags |= CELL_FLAGS_NO_PAINT;
+
+             if (!(cl->flags & CELL_FLAGS_IS_HEADER))
+               io_fprintf (io, "<td");
+             else
+               io_fprintf (io, "<th");
+             if (cl->clazz)
+               io_fprintf (io, " class=\"%s\"", cl->clazz);
+             if (cl->rowspan > 1)
+               io_fprintf (io, " rowspan=\"%d\"", cl->rowspan);
+             if (cl->colspan > 1)
+               io_fprintf (io, " colspan=\"%d\"", cl->colspan);
+             if (cl->align == 'r')
+               io_fprintf (io, " align=\"right\"");
+             else if (cl->align == 'c')
+               io_fprintf (io, " align=\"center\"");
+             if (cl->valign == 't')
+               io_fprintf (io, " valign=\"top\"");
+             else if (cl->valign == 'b')
+               io_fprintf (io, " valign=\"bottom\"");
+             io_fprintf (io, ">");
+             if (cl->w)
+               ml_widget_repaint (cl->w, session, windowid, io);
+             else
+               io_fprintf (io, "&nbsp;");
+             if (!(cl->flags & CELL_FLAGS_IS_HEADER))
+               io_fprintf (io, "</td>\n");
+             else
+               io_fprintf (io, "</th>\n");
+           }
+       }
+
+      io_fprintf (io, "</tr>\n");
+    }
+
+  io_fprintf (io, "</table>");
+}
diff --git a/src/ml_table_layout.h b/src/ml_table_layout.h
new file mode 100644 (file)
index 0000000..d839688
--- /dev/null
@@ -0,0 +1,114 @@
+/* Monolith table layout class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_table_layout.h,v 1.3 2002/11/13 20:46:38 rich Exp $
+ */
+
+#ifndef ML_TABLE_LAYOUT_H
+#define ML_TABLE_LAYOUT_H
+
+#include <ml_widget.h>
+
+struct ml_table_layout;
+typedef struct ml_table_layout *ml_table_layout;
+
+/* Function: new_ml_table_layout - monolith table layout widget
+ * Function: ml_table_layout_pack
+ * Function: ml_table_layout_add_row
+ * Function: ml_table_layout_set_colspan
+ * Function: ml_table_layout_set_rowspan
+ * Function: ml_table_layout_set_align
+ * Function: ml_table_layout_set_valign
+ * Function: ml_table_layout_set_class
+ * Function: ml_table_layout_set_header
+ *
+ * The monolith table layout widget is a very powerful
+ * layout tool. It is modelled on, and indeed implemented using,
+ * HTML <table>s. Table layouts are grids of widgets with
+ * a fixed number of rows and columns. Each widget normally
+ * occupies a single cell of the table, but widgets may occupy
+ * a rectangle of several adjacent cells. All cells in the table
+ * are referenced using the row and column number, with row
+ * and column numbers starting from zero.
+ *
+ * Note that when creating forms, it is often better to use
+ * the simpler @code{ml_multicol_layout} widget (see
+ * @ref{new_ml_multicol_layout(3)}).
+ *
+ * For single row or single column tables, it is usually better
+ * to use @code{ml_horizontal_layout} or @code{ml_vertical_layout}
+ * respectively (see @ref{new_ml_horizontal_layout(3)},
+ * @ref{new_ml_vertical_layout(3)}).
+ *
+ * @code{new_ml_table_layout} creates a new table layout widget
+ * with @code{rows} cells across and @code{cols} cells down. The
+ * cells are numbered @code{0 .. rows-1} across and code{0 .. cols-1}
+ * down.
+ *
+ * The following properties can be changed on tables (see
+ * @ref{ml_widget_set_property(3)}):
+ *
+ * @code{class}: The stylesheet class.
+ *
+ * @code{ml_table_layout_pack} packs a widget at position
+ * @code{(row,col)} within the table. To remove a widget and
+ * leave a cell empty, pack a @code{NULL} widget.
+ *
+ * @code{ml_table_layout_add_row} adds a single row at the end
+ * of the table.
+ *
+ * @code{ml_table_layout_set_colspan} and
+ * @code{ml_table_layout_set_rowspan} set the column span
+ * and row span for a particular table cell respectively.
+ * The col/row-span allow a cell to occupy one or more
+ * adjacent cells in the table (any widgets packed in those
+ * adjacent cells are silently ignored). The default
+ * column and row span for every cell is 1.
+ *
+ * @code{ml_table_layout_set_align} sets the horizontal
+ * alignment for the content of a cell. The possibilities
+ * are @code{"left"}, @code{"center"}
+ * or @code{"right"}, with the default being left-aligned.
+ *
+ * @code{ml_table_layout_set_valign} sets the vertical
+ * alignment for the content of a cell. The possibilities
+ * are @code{"top"}, @code{"middle"}
+ * or @code{"bottom"}, with the default being middle.
+ *
+ * @code{ml_table_layout_set_class} sets the stylesheet class for a
+ * cell (the default being no class).
+ *
+ * @code{ml_table_layout_set_header} sets a boolean flag on a cell. When
+ * try, this is a header cell. Otherwise, this is a normal table body cell.
+ * This can be used in conjunction with stylesheets on the table.
+ *
+ * See also: @ref{new_ml_multicol_layout(3)},
+ * @ref{new_ml_horizontal_layout(3)},
+ * @ref{new_ml_vertical_layout(3)},
+ * @ref{new_ml_widget(3)}.
+ */
+extern ml_table_layout new_ml_table_layout (pool, int rows, int cols);
+extern void ml_table_layout_pack (ml_table_layout, ml_widget, int row, int col);
+extern void ml_table_layout_add_row (ml_table_layout);
+extern void ml_table_layout_set_colspan (ml_table_layout, int row, int col, int colspan);
+extern void ml_table_layout_set_rowspan (ml_table_layout, int row, int col, int rowspan);
+extern void ml_table_layout_set_align (ml_table_layout, int row, int col, const char *align);
+extern void ml_table_layout_set_valign (ml_table_layout, int row, int col, const char *valign);
+extern void ml_table_layout_set_class (ml_table_layout, int row, int col, const char *clazz);
+extern void ml_table_layout_set_header (ml_table_layout, int row, int col, int is_header);
+
+#endif /* ML_TABLE_LAYOUT_H */
diff --git a/src/ml_text_label.c b/src/ml_text_label.c
new file mode 100644 (file)
index 0000000..1e1a5bf
--- /dev/null
@@ -0,0 +1,121 @@
+/* Monolith text label class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_text_label.c,v 1.6 2002/11/23 17:31:01 rich Exp $
+ */
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_smarttext.h"
+#include "ml_text_label.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property properties[];
+
+struct ml_widget_operations text_label_ops =
+  {
+    repaint: repaint,
+    properties: properties,
+  };
+
+struct ml_text_label
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  const char *text;            /* Text to be displayed. */
+
+  /* Various style information. */
+  const char *text_align;
+  const char *colour;
+  const char *font_weight;
+  const char *font_size;
+};
+
+static struct ml_widget_property properties[] =
+  {
+    { name: "text",
+      offset: ml_offsetof (struct ml_text_label, text),
+      type: ML_PROP_STRING },
+    { name: "text.align",
+      offset: ml_offsetof (struct ml_text_label, text_align),
+      type: ML_PROP_STRING },
+    { name: "color",
+      offset: ml_offsetof (struct ml_text_label, colour),
+      type: ML_PROP_STRING },
+    { name: "font.weight",
+      offset: ml_offsetof (struct ml_text_label, font_weight),
+      type: ML_PROP_STRING },
+    { name: "font.size",
+      offset: ml_offsetof (struct ml_text_label, font_size),
+      type: ML_PROP_STRING },
+    { 0 },
+  };
+
+ml_text_label
+new_ml_text_label (pool pool, const char *text)
+{
+  ml_text_label w = pmalloc (pool, sizeof *w);
+
+  w->ops = &text_label_ops;
+  w->pool = pool;
+  w->text = text;
+
+  w->text_align = 0;           /* NULL means default for all these. */
+  w->colour = 0;
+  w->font_weight = 0;
+  w->font_size = 0;
+
+  return w;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_text_label w = (ml_text_label) vw;
+
+  if (w->text)
+    {
+      if (w->text_align || w->colour || w->font_weight || w->font_size)
+       {
+         io_fprintf (io, "<span style=\"");
+         if (w->text_align)
+           io_fprintf (io, "text-align: %s;", w->text_align);
+         if (w->colour)
+           io_fprintf (io, "color: %s;", w->colour);
+         if (w->font_weight)
+           io_fprintf (io, "font-weight: %s;", w->font_weight);
+         if (w->font_size)
+           io_fprintf (io, "font-size: %s;", w->font_size);
+         io_fprintf (io, "\">");
+       }
+
+      ml_plaintext_print (io, w->text);
+
+      if (w->text_align || w->colour || w->font_weight || w->font_size)
+       {
+         io_fprintf (io, "</span>");
+       }
+    }
+}
diff --git a/src/ml_text_label.h b/src/ml_text_label.h
new file mode 100644 (file)
index 0000000..f3bcb68
--- /dev/null
@@ -0,0 +1,64 @@
+/* Monolith text label class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_text_label.h,v 1.4 2003/01/11 16:39:10 rich Exp $
+ */
+
+#ifndef ML_TEXT_LABEL_H
+#define ML_TEXT_LABEL_H
+
+#include <ml_widget.h>
+
+struct ml_text_label;
+typedef struct ml_text_label *ml_text_label;
+
+/* Function: new_ml_text_label - monolith text label widget
+ *
+ * Text labels are simple strings or paragraphs of text. Unlike
+ * the @code{ml_label} widget, HTML sequences are escaped before
+ * being sent to the browser, so @code{ml_text_label}s can contain
+ * @code{<}, @code{>}, @code{&} and so on and these will be displayed
+ * correctly.
+ *
+ * Text labels allow control over the style, size and other aspects
+ * of how the text appears.
+ *
+ * @code{new_ml_text_label} creates a new text label widget.
+ *
+ * The following properties can be changed on text labels (see
+ * @code{ml_widget_set_property(3)}):
+ *
+ * @code{text}: The text displayed on the label.
+ *
+ * @code{text.align}: The text-align style
+ * of the text label. Common alignments are @code{"left"}, @code{"right"}
+ * and @code{"justify"}.
+ *
+ * @code{color}: The color style (text color).
+ *
+ * @code{font.weight}: The font-weight style
+ * of the text label. Common weights are @code{"normal"} and @code{"bold"}.
+ *
+ * @code{font.size}: The font-size style
+ * of the text label. Common sizes are @code{"small"}, @code{"medium"}
+ * and @code{"large"}.
+ *
+ * See also: @ref{new_ml_widget(3)}, W3C CSS2 recommendation.
+ */
+extern ml_text_label new_ml_text_label (pool, const char *text);
+
+#endif /* ML_TEXT_LABEL_H */
diff --git a/src/ml_toggle_button.c b/src/ml_toggle_button.c
new file mode 100644 (file)
index 0000000..7f637c2
--- /dev/null
@@ -0,0 +1,150 @@
+/* Monolith toggle button class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_toggle_button.c,v 1.4 2002/11/07 10:49:02 rich Exp $
+ */
+
+#include "config.h"
+
+#include <pool.h>
+#include <pstring.h>
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_toggle_button.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property properties[];
+
+struct ml_widget_operations toggle_button_ops =
+  {
+    repaint: repaint,
+    properties: properties
+  };
+
+struct ml_toggle_button
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  const char *text;            /* HTML printed for the button. */
+  int state;                   /* 0 = out, non-zero = pushed in */
+  const char *action_id;       /* Action. */
+  void (*fn) (ml_session, void *); /* Callback. */
+  void *data;
+  int is_key;                  /* Key button property. */
+};
+
+static struct ml_widget_property properties[] =
+  {
+    { name: "text",
+      offset: ml_offsetof (struct ml_toggle_button, text),
+      type: ML_PROP_STRING },
+    { name: "button.key",      /* XXX Replace with button.style. */
+      offset: ml_offsetof (struct ml_toggle_button, is_key),
+      type: ML_PROP_BOOL },
+    { 0 }
+  };
+
+static void toggle_me (ml_session session, void *vp);
+
+ml_toggle_button
+new_ml_toggle_button (pool pool, ml_session session, const char *text)
+{
+  ml_toggle_button w = pmalloc (pool, sizeof *w);
+
+  w->ops = &toggle_button_ops;
+  w->pool = pool;
+  w->text = text;
+  w->state = 0;
+  w->is_key = 0;
+  w->fn = 0;
+  w->data = 0;
+
+  /* Register the action for this toggle button. */
+  w->action_id = ml_register_action (session, toggle_me, w);
+
+  return w;
+}
+
+/* This function is called whenever the button is toggled. */
+static void
+toggle_me (ml_session session, void *vp)
+{
+  ml_toggle_button w = (ml_toggle_button) vp;
+
+  w->state = !w->state;
+
+  if (w->fn)                   /* Call the user's callback function. */
+    w->fn (session, w->data);
+}
+
+void
+ml_toggle_button_set_callback (ml_toggle_button w,
+                              void (*fn)(ml_session, void *),
+                              ml_session session, void *data)
+{
+  w->fn = fn;
+  w->data = data;
+}
+
+void
+ml_toggle_button_set_state (ml_toggle_button w, int state)
+{
+  w->state = state;
+}
+
+int
+ml_toggle_button_get_state (ml_toggle_button w)
+{
+  return w->state;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_toggle_button w = (ml_toggle_button) vw;
+  pool pool = ml_session_pool (session);
+
+  if (w->text)
+    {
+      const char *clazz, *link;
+
+      if (!w->is_key)
+       {
+         if (!w->state)
+           clazz = "ml_toggle_button";
+         else
+           clazz = "ml_toggle_button_pressed";
+       }
+      else
+       {
+         if (!w->state)
+           clazz = "ml_toggle_button_key";
+         else
+           clazz = "ml_toggle_button_key_pressed";
+       }
+
+      link = psprintf (pool, "%s?ml_action=%s&ml_window=%s",
+                      ml_session_script_name (session),
+                      w->action_id,
+                      windowid);
+
+      io_fprintf (io, "<a class=\"%s\" href=\"%s\">%s</a>",
+                 clazz, link, w->text);
+    }
+}
diff --git a/src/ml_toggle_button.h b/src/ml_toggle_button.h
new file mode 100644 (file)
index 0000000..e4abd06
--- /dev/null
@@ -0,0 +1,78 @@
+/* Monolith toggle button class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_toggle_button.h,v 1.2 2002/10/30 21:03:04 rich Exp $
+ */
+
+#ifndef ML_TOGGLE_BUTTON_H
+#define ML_TOGGLE_BUTTON_H
+
+#include "monolith.h"
+
+struct ml_toggle_button;
+typedef struct ml_toggle_button *ml_toggle_button;
+
+/* Function: new_ml_toggle_button - monolith toggle button widget
+ * Function: ml_toggle_button_set_state
+ * Function: ml_toggle_button_get_state
+ * Function: ml_toggle_button_set_callback
+ *
+ * A toggle button widget is similar to an ordinary button
+ * (see @ref{new_ml_button(3)}) except that it has two states,
+ * 0 and non-zero (out, or pushed in respectively). You can
+ * query or set the current state of the toggle button, and
+ * you can arrange for a callback function to be called when
+ * the state changes.
+ *
+ * Note that toggle buttons cannot be used as part of forms.
+ * Use an @code{ml_form_checkbox} instead.
+ *
+ * @code{new_ml_toggle_button} creates a new toggle button widget.
+ *
+ * The following properties can be changed on toggle buttons (see
+ * @ref{ml_widget_set_property(3)}):
+ *
+ * @code{text}: The HTML text printed
+ * on the button. Note that the text string must be either
+ * static, or already allocated in the button's pool, or allocated
+ * in a pool with a longer lifetime than the button. If the text
+ * is set to @code{NULL} then this has the same effect as setting
+ * the text to the empty string, which is not very useful because
+ * the button can never be pressed.
+ *
+ * @code{button.key} (boolean): The "key" property of
+ * the button. This should be set on buttons where the text is
+ * a single letter, suitable for, say, calculator keys. All key
+ * buttons are rendered at a fixed width of "1em", resulting in
+ * a more pleasing overall effect.
+ *
+ * @code{ml_toggle_button_(set|get)_state} updates or queries the
+ * current state of the button. The state is either 0 (the default)
+ * meaning the button is out, or non-zero meaning the button is
+ * pushed in.
+ *
+ * @code{ml_toggle_button_set_callback} updates the callback
+ * function which is invoked when the button changes state
+ * (either from "out" to "in", or from "in" to "out"). Use
+ * @code{ml_toggle_button_get_state} to find the new state.
+ */
+extern ml_toggle_button new_ml_toggle_button (pool pool, ml_session, const char *text);
+extern void ml_toggle_button_set_state (ml_toggle_button, int state);
+extern int ml_toggle_button_get_state (ml_toggle_button);
+extern void ml_toggle_button_set_callback (ml_toggle_button, void (*fn)(ml_session, void *), ml_session, void *);
+
+#endif /* ML_TOGGLE_BUTTON_H */
diff --git a/src/ml_vertical_layout.c b/src/ml_vertical_layout.c
new file mode 100644 (file)
index 0000000..3995f7c
--- /dev/null
@@ -0,0 +1,159 @@
+/* Monolith vertical layout class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_vertical_layout.c,v 1.2 2003/01/12 22:12:42 rich Exp $
+ */
+
+#include "config.h"
+
+#include <pthr_iolib.h>
+
+#include "ml_widget.h"
+#include "monolith.h"
+#include "ml_vertical_layout.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+static struct ml_widget_property properties[];
+
+struct ml_widget_operations vertical_layout_ops =
+  {
+    repaint: repaint,
+    properties: properties
+  };
+
+struct ml_vertical_layout
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  const char *clazz;           /* Stylesheet class. */
+  vector v;                    /* Vector of widgets. */
+};
+
+static struct ml_widget_property properties[] =
+  {
+    { name: "class",
+      offset: ml_offsetof (struct ml_vertical_layout, clazz),
+      type: ML_PROP_STRING },
+    { 0 }
+  };
+
+ml_vertical_layout
+new_ml_vertical_layout (pool pool)
+{
+  ml_vertical_layout w = pmalloc (pool, sizeof *w);
+
+  w->ops = &vertical_layout_ops;
+  w->pool = pool;
+  w->clazz = 0;
+  w->v = new_vector (pool, ml_widget);
+
+  return w;
+}
+
+void
+ml_vertical_layout_push_back (ml_vertical_layout w, ml_widget _w)
+{
+  vector_push_back (w->v, _w);
+}
+
+void
+ml_vertical_layout_pack (ml_vertical_layout w, ml_widget _w)
+{
+  vector_push_back (w->v, _w);
+}
+
+ml_widget
+ml_vertical_layout_pop_back (ml_vertical_layout w)
+{
+  ml_widget _w;
+
+  vector_pop_back (w->v, _w);
+  return _w;
+}
+
+void
+ml_vertical_layout_push_front (ml_vertical_layout w, ml_widget _w)
+{
+  vector_push_front (w->v, _w);
+}
+
+ml_widget
+ml_vertical_layout_pop_front (ml_vertical_layout w)
+{
+  ml_widget _w;
+
+  vector_pop_front (w->v, _w);
+  return _w;
+}
+
+ml_widget
+ml_vertical_layout_get (ml_vertical_layout w, int i)
+{
+  ml_widget _w;
+
+  vector_get (w->v, i, _w);
+  return _w;
+}
+
+void
+ml_vertical_layout_insert (ml_vertical_layout w, int i, ml_widget _w)
+{
+  vector_insert (w->v, i, _w);
+}
+
+void
+ml_vertical_layout_replace (ml_vertical_layout w, int i, ml_widget _w)
+{
+  vector_replace (w->v, i, _w);
+}
+
+void
+ml_vertical_layout_erase (ml_vertical_layout w, int i)
+{
+  vector_erase (w->v, i);
+}
+
+void
+ml_vertical_layout_clear (ml_vertical_layout w)
+{
+  vector_clear (w->v);
+}
+
+int
+ml_vertical_layout_size (ml_vertical_layout w)
+{
+  return vector_size (w->v);
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_vertical_layout w = (ml_vertical_layout) vw;
+  int i;
+
+  for (i = 0; i < vector_size (w->v); ++i)
+    {
+      ml_widget _w;
+
+      vector_get (w->v, i, _w);
+      io_fputs ("<div", io);
+      if (w->clazz) io_fprintf (io, " class=\"%s\"", w->clazz);
+      io_fputc ('>', io);
+      ml_widget_repaint (_w, session, windowid, io);
+      io_fputs ("</div>\n", io);
+    }
+}
diff --git a/src/ml_vertical_layout.h b/src/ml_vertical_layout.h
new file mode 100644 (file)
index 0000000..bbfc236
--- /dev/null
@@ -0,0 +1,78 @@
+/* Monolith vertical layout class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_vertical_layout.h,v 1.2 2003/01/12 22:12:42 rich Exp $
+ */
+
+#ifndef ML_VERTICAL_LAYOUT_H
+#define ML_VERTICAL_LAYOUT_H
+
+#include <ml_widget.h>
+
+struct ml_vertical_layout;
+typedef struct ml_vertical_layout *ml_vertical_layout;
+
+/* Function: new_ml_vertical_layout - monolith vertical_layout widget
+ * Function: ml_vertical_layout_push_back
+ * Function: ml_vertical_layout_pop_back
+ * Function: ml_vertical_layout_push_front
+ * Function: ml_vertical_layout_pop_front
+ * Function: ml_vertical_layout_get
+ * Function: ml_vertical_layout_insert
+ * Function: ml_vertical_layout_replace
+ * Function: ml_vertical_layout_erase
+ * Function: ml_vertical_layout_clear
+ * Function: ml_vertical_layout_size
+ * Function: ml_vertical_layout_pack
+ *
+ * A vertical layout widget is a simple type of layout which can be
+ * thought of as a specialised one-column table.
+ *
+ * @code{new_ml_vertical_layout} creates a new vertical layout widget.
+ *
+ * Underlying the vertical layout widget is a simple c2lib vector, and the
+ * other access functions use the same notation as the equivalent
+ * c2lib @code{vector_*} functions. Go to the SEE ALSO section
+ * below to see how to manipulate widgets within a vertical layout.
+ *
+ * @code{ml_vertical_layout_pack} is equivalent to
+ * @code{ml_vertical_layout_push_back}: it appends the widget to the
+ * end of the current vector of widgets.
+ *
+ * The following properties can be changed on vertical layouts (see
+ * @ref{ml_widget_set_property(3)}):
+ *
+ * @code{class}: The stylesheet class.
+ * See also: @ref{vector_push_back(3)}, @ref{vector_pop_back(3)},
+ * @ref{vector_push_front(3)}, @ref{vector_pop_front(3)},
+ * @ref{vector_get(3)}, @ref{vector_insert(3)}, @ref{vector_replace(3)},
+ * @ref{vector_erase(3)}, @ref{vector_clear(3)}, @ref{vector_size(3)}.
+ */
+extern ml_vertical_layout new_ml_vertical_layout (pool pool);
+extern void ml_vertical_layout_push_back (ml_vertical_layout, ml_widget);
+extern ml_widget ml_vertical_layout_pop_back (ml_vertical_layout);
+extern void ml_vertical_layout_push_front (ml_vertical_layout, ml_widget);
+extern ml_widget ml_vertical_layout_pop_front (ml_vertical_layout);
+extern ml_widget ml_vertical_layout_get (ml_vertical_layout, int i);
+extern void ml_vertical_layout_insert (ml_vertical_layout, int i, ml_widget);
+extern void ml_vertical_layout_replace (ml_vertical_layout, int i, ml_widget);
+extern void ml_vertical_layout_erase (ml_vertical_layout, int i);
+extern void ml_vertical_layout_clear (ml_vertical_layout);
+extern int ml_vertical_layout_size (ml_vertical_layout);
+extern void ml_vertical_layout_pack (ml_vertical_layout, ml_widget);
+
+#endif /* ML_VERTICAL_LAYOUT_H */
diff --git a/src/ml_widget.c b/src/ml_widget.c
new file mode 100644 (file)
index 0000000..2651cc2
--- /dev/null
@@ -0,0 +1,182 @@
+/* Monolith widget class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_widget.c,v 1.5 2002/11/29 10:43:03 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "monolith.h"
+#include "ml_widget.h"
+
+/* This is what generic widget objects *actually* look like. */
+struct widget
+{
+  struct ml_widget_operations *ops;
+};
+
+void
+ml_widget_repaint (void *vw, ml_session session, const char *windowid,
+                  io_handle io)
+{
+  struct widget *w = (struct widget *) vw;
+
+  if (w->ops->repaint) w->ops->repaint (vw, session, windowid, io);
+}
+
+const struct ml_widget_property *
+ml_widget_get_properties (void *vw)
+{
+  struct widget *w = (struct widget *) vw;
+
+  return w->ops->properties;
+}
+
+void
+ml_widget_set_property (void *vw, const char *property_name, ...)
+{
+  struct widget *w = (struct widget *) vw;
+  struct ml_widget_property *properties = w->ops->properties;
+  va_list args;
+
+  /* If you hit this assertion, then you've tried to set properties
+   * on a widget which isn't property-aware.
+   */
+  assert (properties != 0);
+
+  /* Search for the appropriate property. */
+  while (properties->name)
+    {
+      if (strcmp (properties->name, property_name) == 0)
+       {
+         /* Read-only? */
+         assert (!(properties->flags & ML_PROP_READ_ONLY));
+
+         va_start (args, property_name);
+
+         /* Update it. */
+         switch (properties->type) {
+         case ML_PROP_STRING:
+           {
+             char **v = (char **) (vw + properties->offset);
+             *v = va_arg (args, char *);
+             break;
+           }
+         case ML_PROP_INT:
+           {
+             int *v = (int *) (vw + properties->offset);
+             *v = va_arg (args, int);
+             break;
+           }
+         case ML_PROP_CHAR:
+           {
+             char *v = (char *) (vw + properties->offset);
+             *v = va_arg (args, int); /* sic */
+             break;
+           }
+         case ML_PROP_WIDGET:
+           {
+             ml_widget *v = (ml_widget *) (vw + properties->offset);
+             *v = va_arg (args, ml_widget);
+             break;
+           }
+         default:
+           abort ();           /* Unknown type. */
+         }
+
+         va_end (args);
+
+         if (properties->on_set) properties->on_set (vw);
+
+         return;
+       }
+
+      properties++;
+    }
+
+  /* If you reach here, then you've tried to update a non-existant
+   * property in a widget.
+   */
+  fprintf (stderr,
+          "ml_widget_set_property: unknown property name: %s\n",
+          property_name);
+  abort ();
+}
+
+void
+_ml_widget_get_property (void *vw, const char *property_name, void *varptr)
+{
+  struct widget *w = (struct widget *) vw;
+  struct ml_widget_property *properties = w->ops->properties;
+  int size;
+
+  /* If you hit this assertion, then you've tried to get properties
+   * on a widget which isn't property-aware.
+   */
+  assert (properties != 0);
+
+  /* Search for the appropriate property. */
+  while (properties->name)
+    {
+      if (strcmp (properties->name, property_name) == 0)
+       {
+         /* Write-only? */
+         assert (!(properties->flags & ML_PROP_WRITE_ONLY));
+
+         if (properties->on_get) properties->on_get (vw);
+
+         /* Retrieve it. */
+         switch (properties->type) {
+         case ML_PROP_STRING:
+           size = sizeof (char *); break;
+         case ML_PROP_INT:
+           size = sizeof (int); break;
+         case ML_PROP_CHAR:
+           size = sizeof (char); break;
+         case ML_PROP_WIDGET:
+           size = sizeof (ml_widget); break;
+         default:
+           abort ();           /* Unknown type. */
+         }
+
+         memcpy (varptr, vw + properties->offset, size);
+
+         return;
+       }
+
+      properties++;
+    }
+
+  /* If you reach here, then you've tried to update a non-existant
+   * property in a widget.
+   */
+  fprintf (stderr,
+          "_ml_widget_get_property: unknown property name: %s\n",
+          property_name);
+  abort ();
+}
diff --git a/src/ml_widget.h b/src/ml_widget.h
new file mode 100644 (file)
index 0000000..58bdb63
--- /dev/null
@@ -0,0 +1,133 @@
+/* Monolith widget class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_widget.h,v 1.8 2002/11/29 10:43:03 rich Exp $
+ */
+
+#ifndef ML_WIDGET_H
+#define ML_WIDGET_H
+
+#include <stdarg.h>
+
+#include <pthr_iolib.h>
+
+struct ml_session;
+
+/* Best to treat widgets as opaque, except in ml_widget.c itself where
+ * we know that they in fact contain a pointer to the list of widget
+ * operations.
+ */
+typedef void *ml_widget;
+
+/* Widget property. */
+struct ml_widget_property
+{
+  const char *name;            /* Name of the property. */
+  int offset;                  /* Offset into widget structure. */
+  int type;                    /* Property type. */
+#define ML_PROP_STRING    1
+#define ML_PROP_INT       2
+#define ML_PROP_BOOL      ML_PROP_INT
+#define ML_PROP_CHAR      3
+#define ML_PROP_WIDGET    4
+  void (*on_set) (ml_widget w); /* Called after property is set. */
+  void (*on_get) (ml_widget w); /* Called before property is got. */
+  int flags;                   /* Flags. */
+#define ML_PROP_READ_ONLY  1
+#define ML_PROP_WRITE_ONLY 2
+};
+
+/* A pointer to this struct must occupy the first slot in every
+ * widget-type structure we define elsewhere.
+ */
+struct ml_widget_operations
+{
+  /* All widgets must have a repaint function. */
+  void (*repaint) (void *widget, struct ml_session *session,
+                  const char *windowid, io_handle io);
+
+  /* List of properties (NULL = no properties). */
+  struct ml_widget_property *properties;
+};
+
+/* Function: ml_widget_repaint - Operations on generic monolith widgets.
+ * Function: ml_widget_get_properties
+ * Function: ml_widget_set_property
+ * Function: ml_widget_get_property
+ * Function: _ml_widget_get_property
+ *
+ * @code{ml_widget_repaint} calls the repaint function on a generic
+ * widget.
+ *
+ * The @code{*property} functions are concerned with widget properties.
+ * Properties are generic attributes of a widget which can be read and
+ * updated using @code{ml_widget_set_property} and
+ * @code{ml_widget_get_property}.
+ *
+ * @code{ml_widget_get_properties} returns a list of the properties
+ * available to be changed in the current widget. The list returned
+ * can be @code{NULL} meaning that this widget does not support
+ * properties. @code{struct ml_widget_property} is defined in
+ * @code{<ml_widget.h>}.
+ *
+ * Properties have consistent names. These are some of the property
+ * names defined so far:
+ *
+ * @code{button.style}: For buttons, sets the style of the button,
+ * either @code{"default"}, @code{"key"}, @code{"compact"} or
+ * @code{"link"}.
+ *
+ * @code{class}: Many widgets support this. It sets or overrides the
+ * class of the top-level HTML element. You can use this, in conjunction
+ * with a stylesheet, to dramatically change the look and feel of a
+ * particular widget. In fact, this is the recommended method for changing
+ * the style for all widgets except text labels.
+ *
+ * @code{color}: The color style (text color).
+ *
+ * @code{font.size}: Font size for text displayed on a label or
+ * button. Common sizes are @code{"small"}, @code{"medium"} or
+ * @code{"large"}.
+ *
+ * @code{font.weight}: Font weight for text displayed on a label or
+ * button. Common weights are @code{"normal"} and @code{"bold"}.
+ *
+ * @code{form.select.size}: On form select input boxes, the numbers of
+ * rows.
+ *
+ * @code{form.select.multiple}: On form select input boxes, whether
+ * the input is single- or multiple-select.
+ *
+ * @code{image}: On images, the path to the image.
+ *
+ * @code{text}: The text displayed on a label or button.
+ *
+ * @code{text.align}: Alignment of text displayed on a label or
+ * button. Possible values are: @code{"left"}, @code{"right"},
+ * @code{"justify"}.
+ *
+ * @code{title}: Many widgets support this to give the widget a
+ * title or "tooltip".
+ *
+ */
+extern void ml_widget_repaint (ml_widget widget, struct ml_session *, const char *windowid, io_handle);
+extern const struct ml_widget_property *ml_widget_get_properties (ml_widget widget);
+extern void ml_widget_set_property (ml_widget widget, const char *property_name, ...);
+#define ml_widget_get_property(widget,property_name,var) (_ml_widget_get_property ((widget), (property_name), &(var)))
+extern void _ml_widget_get_property (ml_widget widget, const char *property_name, void *varptr);
+
+#endif /* ML_WIDGET_H */
diff --git a/src/ml_window.c b/src/ml_window.c
new file mode 100644 (file)
index 0000000..f8576eb
--- /dev/null
@@ -0,0 +1,434 @@
+/* Monolith window class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_window.c,v 1.8 2003/04/30 13:15:34 rich Exp $
+ */
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <vector.h>
+#include <pstring.h>
+
+#include <pthr_http.h>
+
+#include "monolith.h"
+#include "ml_window.h"
+
+struct ml_window
+{
+  pool pool;                   /* Pool for allocations. */
+  const char *windowid;                /* Window ID. */
+
+  /* For ordinary windows: */
+  ml_widget w;                 /* Packed widget. */
+  int headers_flag;            /* If set, emit start, end of page. */
+  const char *title;           /* Title of the window. */
+  const char *stylesheet;      /* Stylesheet for the window. */
+  const char *charset;         /* Character encoding. */
+  int refresh;                 /* Refresh period (0 = no refresh). */
+  int scroll_to_x, scroll_to_y;        /* Scroll to (x, y). */
+
+  /* For framesets: */
+  const char *rows, *cols;     /* Layout. */
+  vector frames;               /* Frames (vector of struct
+                                * ml_frame_description). */
+  vector actions;              /* Action IDs (one per frame). */
+
+  /* For redirects. */
+  const char *uri;             /* URI to redirect to. */
+
+  /* Javascript cookies to set (see _ml_window_set_cookie_with_javascript). */
+  vector js_cookies;
+};
+
+static int next_window_id = 0;
+
+ml_window
+new_ml_window (ml_session session, pool pool)
+{
+  ml_window w = pmalloc (pool, sizeof *w);
+
+  w->pool = pool;
+  w->w = 0;
+  w->headers_flag = 1;
+  w->title = 0;
+  /* XXX Eventually take this from the default theme for the application? */
+  w->stylesheet = "/ml-styles/default.css";
+  w->charset = "utf-8";
+  w->refresh = 0;
+  w->scroll_to_x = w->scroll_to_y = 0;
+
+  w->rows = w->cols = 0;
+  w->frames = 0;
+  w->actions = 0;
+
+  w->uri = 0;
+  w->js_cookies = new_vector (pool, const char *);
+
+  /* Register and set current window. */
+  w->windowid = pitoa (pool, ++next_window_id);
+  _ml_session_set_current_window (session, w, w->windowid);
+
+  return w;
+}
+
+void
+ml_window_pack (ml_window w, ml_widget _w)
+{
+  w->w = _w;
+}
+
+void
+ml_window_set_headers_flag (ml_window w, int headers_flag)
+{
+  w->headers_flag = headers_flag;
+}
+
+int
+ml_window_get_headers_flag (ml_window w)
+{
+  return w->headers_flag;
+}
+
+void
+ml_window_set_title (ml_window w, const char *title)
+{
+  w->title = title;
+}
+
+const char *
+ml_window_get_title (ml_window w)
+{
+  return w->title;
+}
+
+void
+ml_window_set_stylesheet (ml_window w, const char *stylesheet)
+{
+  w->stylesheet = stylesheet;
+}
+
+const char *
+ml_window_get_stylesheet (ml_window w)
+{
+  return w->stylesheet;
+}
+
+void
+ml_window_set_charset (ml_window w, const char *_charset)
+{
+  w->charset = _charset;
+}
+
+const char *
+ml_window_get_charset (ml_window w)
+{
+  return w->charset;
+}
+
+void
+ml_window_set_refresh (ml_window w, int refresh)
+{
+  w->refresh = refresh;
+}
+
+int
+ml_window_get_refresh (ml_window w)
+{
+  return w->refresh;
+}
+
+void
+ml_window_scroll_to (ml_window w, int x, int y)
+{
+  w->scroll_to_x = x;
+  w->scroll_to_y = y;
+}
+
+static void update_actions (ml_window w, ml_session session);
+
+ml_window
+new_ml_frameset (ml_session session, pool pool,
+                const char *rows, const char *cols, vector frames)
+{
+  ml_window w = new_ml_window (session, pool);
+
+  w->rows = rows;
+  w->cols = cols;
+  w->frames = frames;
+  update_actions (w, session);
+
+  return w;
+}
+
+void
+ml_frameset_set_description (ml_window w, ml_session session,
+                            const char *rows, const char *cols, vector frames)
+{
+  w->rows = rows;
+  w->cols = cols;
+  w->frames = frames;
+  update_actions (w, session);
+}
+
+void
+ml_frameset_set_title (ml_window w, const char *title)
+{
+  w->title = title;
+}
+
+const char *
+ml_frameset_get_title (ml_window w)
+{
+  return w->title;
+}
+
+static void
+update_actions (ml_window w, ml_session session)
+{
+  int i;
+  const char *actionid;
+  struct ml_frame_description frame;
+
+  if (w->actions)
+    {
+      /* Unregister the old actions. */
+      for (i = 0; i < vector_size (w->actions); ++i)
+       {
+         vector_get (w->actions, i, actionid);
+         ml_unregister_action (session, actionid);
+       }
+      vector_clear (w->actions);
+    }
+  else
+    w->actions = new_vector (w->pool, const char *);
+
+  if (w->frames && vector_size (w->frames) > 0)
+    {
+      /* Register new actions. */
+      for (i = 0; i < vector_size (w->frames); ++i)
+       {
+         vector_get (w->frames, i, frame);
+         actionid = ml_register_action (session, frame.fn, frame.data);
+         vector_push_back (w->actions, actionid);
+       }
+    }
+}
+
+ml_window
+new_ml_redirect (ml_session session, pool pool,
+                const char *uri)
+{
+  ml_window w = new_ml_window (session, pool);
+
+  w->uri = uri;
+
+  return w;
+}
+
+void
+ml_redirect_set_uri (ml_window w, const char *uri)
+{
+  w->uri = uri;
+}
+
+const char *
+ml_redirect_get_uri (ml_window w)
+{
+  return w->uri;
+}
+
+void
+_ml_window_notify_begin_response (ml_window w)
+{
+  vector_clear (w->js_cookies);
+}
+
+int
+_ml_window_get_response_code (ml_window w)
+{
+  if (! w->uri) return 200;
+  else return 301;
+}
+
+const char *
+_ml_window_get_response_name (ml_window w)
+{
+  if (! w->uri) return "OK";
+  else return "Moved Permanently";
+}
+
+/* To allow shared object scripts to be embedded in pages using
+ * mod_include we must set the session cookie using Javascript. This
+ * is because mod_include doesn't propagate the Set-Cookie header from
+ * a #include to the outer set of headers.
+ *
+ * NB. 'cookie' is allocated on the thread pool.
+ */
+void
+_ml_window_set_cookie_with_javascript (ml_window w,
+                                      const char *cookie)
+{
+  vector_push_back (w->js_cookies, cookie);
+}
+
+static inline void
+set_cookies_with_javascript (ml_window w, io_handle io)
+{
+  while (vector_size (w->js_cookies) > 0)
+    {
+      const char *cookie;
+
+      vector_pop_front (w->js_cookies, cookie);
+      io_fprintf (io,
+                 "<script language=\"javascript\"><!--\n"
+                 "document.cookie = '%s';\n"
+                 "//--></script>\n",
+                 cookie);
+    }
+}
+
+void
+_ml_window_send_headers (ml_window w, pool thread_pool,
+                        http_response http_response)
+{
+  if (! w->uri)
+    {
+      /* Send the Content-Type: header. */
+      http_response_send_header
+       (http_response,
+        "Content-Type", psprintf (thread_pool,
+                                  "text/html; charset=%s",
+                                  w->charset));
+
+      /* Refresh period? */
+      if (w->refresh != 0)
+       http_response_send_header
+         (http_response,
+          "Refresh", pitoa (thread_pool, w->refresh));
+    }
+  else
+    {
+      /* Send the Location: header. */
+      http_response_send_header (http_response, "Location", w->uri);
+
+      /* Send the Content-Length: header. */
+      http_response_send_header (http_response, "Content-Length", "0");
+    }
+}
+
+const char *
+_ml_window_get_windowid (ml_window w)
+{
+  return w->windowid;
+}
+
+void
+_ml_window_repaint (ml_window w, ml_session session, io_handle io)
+{
+  if (!w->frames && !w->uri)   /* Ordinary window. */
+    {
+      if (w->headers_flag)
+       {
+         io_fprintf
+           (io,
+            "<!DOCTYPE html "
+            "PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "
+            "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
+            "<html xmlns=\"http://www.w3.org/1999/xhtml\" "
+            "xml:lang=\"en\" lang=\"en\">\n"
+            "<head>\n");
+
+         if (w->title)
+           io_fprintf (io, "<title>%s</title>\n", w->title);
+
+         if (w->stylesheet)
+           io_fprintf (io,
+                       "<link rel=\"stylesheet\" "
+                       "href=\"%s\" type=\"text/css\">\n",
+                       w->stylesheet);
+
+         io_fprintf (io, "</head><body>\n");
+       }
+
+      set_cookies_with_javascript (w, io);
+
+      if (w->w)
+       ml_widget_repaint (w->w, session, w->windowid, io);
+
+      if (w->scroll_to_x > 0 || w->scroll_to_y > 0)
+       {
+         io_fprintf
+           (io,
+            "<script language=\"javascript\"><!--\n"
+            "window.scrollTo (%d, %d);\n"
+            "//--></script>\n",
+            w->scroll_to_x, w->scroll_to_y);
+       }
+
+      if (w->headers_flag)
+       io_fprintf (io, "</body></html>\n");
+    }
+  else if (w->frames)          /* Frameset. */
+    {
+      int i;
+
+      io_fprintf
+       (io,
+        "<!DOCTYPE html "
+        "PUBLIC \"-//W3C//DTD XHTML 1.0 Frameset//EN\" "
+        "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd\">\n"
+        "<html xmlns=\"http://www.w3.org/1999/xhtml\" "
+        "xml:lang=\"en\" lang=\"en\">\n"
+        "<head>\n");
+
+      if (w->title)
+       io_fprintf (io, "<title>%s</title>\n", w->title);
+
+      io_fprintf (io, "</head><frameset");
+
+      if (w->rows)
+       io_fprintf (io, " rows=\"%s\"", w->rows);
+      if (w->cols)
+       io_fprintf (io, " cols=\"%s\"", w->cols);
+
+      io_fprintf (io, ">");
+
+      for (i = 0; i < vector_size (w->frames); ++i)
+       {
+         /* struct ml_frame_description frame; */
+         const char *actionid;
+
+         /* vector_get (w->frames, i, frame); */
+         vector_get (w->actions, i, actionid);
+
+         io_fprintf (io, "<frame src=\"%s?ml_action=%s\" />",
+                     ml_session_script_name (session), actionid);
+       }
+
+      io_fprintf (io, "</frameset></html>\n");
+    }
+  else                         /* Redirect. */
+    {
+      /* Do nothing. */
+    }
+}
diff --git a/src/ml_window.h b/src/ml_window.h
new file mode 100644 (file)
index 0000000..138b0ee
--- /dev/null
@@ -0,0 +1,198 @@
+/* Monolith window class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_window.h,v 1.8 2003/04/30 13:15:34 rich Exp $
+ */
+
+#ifndef ML_WINDOW_H
+#define ML_WINDOW_H
+
+#include <pthr_iolib.h>
+#include <pthr_http.h>
+#include <ml_widget.h>
+
+struct ml_window;
+typedef struct ml_window *ml_window;
+
+struct ml_session;
+
+struct ml_frame_description
+{
+  void (*fn) (struct ml_session *, void *data);
+  void *data;
+};
+
+/* Function: new_ml_window - monolith windows, framesets, redirects
+ * Function: ml_window_pack
+ * Function: ml_window_set_headers_flag
+ * Function: ml_window_get_headers_flag
+ * Function: ml_window_set_title
+ * Function: ml_window_get_title
+ * Function: ml_window_set_stylesheet
+ * Function: ml_window_get_stylesheet
+ * Function: ml_window_set_charset
+ * Function: ml_window_get_charset
+ * Function: ml_window_set_refresh
+ * Function: ml_window_get_refresh
+ * Function: ml_window_scroll_to
+ * Function: new_ml_frameset
+ * Function: ml_frameset_set_description
+ * Function: ml_frameset_set_title
+ * Function: ml_frameset_get_title
+ * Function: new_ml_redirect
+ * Function: ml_redirect_set_uri
+ * Function: ml_redirect_get_uri
+ *
+ * A "window" is a top-level window. Every application has at
+ * least one window, created in the main function. A window is
+ * just a wrapper. To actually do anything, you must pack a
+ * single widget inside the window. Windows can only take a
+ * single widget. If you want more than one widget to appear
+ * inside a window, then you must pack them into some sort
+ * of layout widget first, and pack the layout widget into
+ * the window.
+ *
+ * A "frameset" is used to create framesets (several ordinary
+ * windows packed into one top-level window, with independent
+ * scrolling capabilities). Framesets may contain windows or
+ * other framesets, or a mixture of both.
+ *
+ * A "redirect" is a window/frame that shows a static page
+ * (in fact anything which can be reached using a URI - pages, images,
+ * CGI scripts, even other Monolith applications).
+ *
+ * Windows, framesets and redirects are actually so
+ * similar, that I have included them in the same class (and also so
+ * that other code can deal with an opaque @code{ml_window} pointer
+ * and not have to worry about whether it is a window,
+ * frameset, etc.).
+ *
+ * Monolith windows are not widgets (unlike many of the
+ * other classes in monolith).
+ *
+ * @code{new_ml_window} creates a new monolith window.
+ *
+ * @code{ml_window_pack} packs a widget into the window. Since a
+ * window can only contain a single widget, subsequent calls to
+ * this function overwrite the packed widget. (Note: this call
+ * does not apply to framesets).
+ *
+ * @code{ml_window_(set|get)_headers_flag} is a somewhat esoteric flag
+ * that you will almost never need to change. When set (the default),
+ * the window widget outputs the opening @code{<html>}, @code{<head/>},
+ * @code{<body>} and closing @code{</body>}, @code{</html>}. If
+ * cleared, then these are not output. Almost the only place where it
+ * is useful to clear this flag is when using the @code{ml_msp}
+ * (monolith server-parsed pages) widget directly inside a window
+ * (see @ref{new_ml_msp(3)}).
+ *
+ * @code{ml_window_(set|get)_title} changes the title of
+ * the window. The title of the window defaults to no title
+ * at all, so it is a good idea to set this.
+ *
+ * @code{ml_window_(set|get)_stylesheet} changes the stylesheet
+ * of the page. Monolith default stylesheets are installed in
+ * @code{/ml-styles/}, and the default stylesheet is
+ * @code{/ml-styles/default.css} (supplied in the monolith
+ * distribution). Stylesheets are used to 'theme' monolith
+ * applications.
+ *
+ * @code{ml_window_(set|get)_charset} changes the character
+ * encoding associated with the window. Because of limitations
+ * in HTML, only a single charset can be associated with all
+ * of the widgets in a window. The default charset is
+ * @code{utf-8}.
+ *
+ * @code{ml_window_(set|get)_refresh} changes the refresh
+ * period. This is an integer representing a number of seconds.
+ * If this is greater than zero, then the browser will automatically
+ * refetch the page after this many seconds. The default is zero
+ * which means no automatic refresh. It is not recommended that
+ * you set this in ordinary applications.
+ *
+ * @code{ml_window_scroll_to} scrolls the window to the absolute
+ * (@code{x}, @code{y}) pixel position given. This is not supported by
+ * all browsers.
+ *
+ * @code{new_ml_frameset} creates a new frameset. @code{rows} and
+ * @code{cols} define the number of frames and their layout.  You can
+ * use @code{rows} and @code{cols} to create frameset layouts
+ * including grids, as described in the HTML 4 standard
+ * (@code{http://www.w3.org/TR/html401/}). To create nested framesets,
+ * use a frame which generates a frameset instead of a
+ * window. @code{frames} is a vector of @code{struct ml_frame_description}
+ * structures (note: the structures themselves, not pointers to the
+ * structures). @code{struct ml_frame_description} contains the
+ * following fields:
+ *
+ * @code{fn}: The function which is called generate the frame
+ * (or nested frameset). This function must call either
+ * @code{new_ml_window} or @code{new_ml_frameset}. The function
+ * is prototyped as @code{void fn (ml_session session, void *data);}.
+ *
+ * @code{data}: Data pointer passed to this function.
+ *
+ * @code{ml_frameset_set_description} allows the frameset description
+ * to be updated.
+ *
+ * @code{ml_frameset_(set|get)_title} updates the window title.
+ *
+ * @code{new_ml_redirect} creates a new redirect. @code{uri}
+ * is the URI of the static page (etc.) containing the actual
+ * content for this window/frame.
+ *
+ * @code{ml_redirect_(set|get)_uri} updates the URI of the static
+ * page being displayed. Note that setting the URI will not necessarily
+ * repaint the contents of the window.
+ *
+ * See also: @ref{ml_ok_window(3)}.
+ */
+extern ml_window new_ml_window (struct ml_session *, pool pool);
+extern void ml_window_pack (ml_window, ml_widget);
+extern void ml_window_set_headers_flag (ml_window, int headers_flag);
+extern int ml_window_get_headers_flag (ml_window);
+extern void ml_window_set_title (ml_window, const char *title);
+extern const char *ml_window_get_title (ml_window);
+extern void ml_window_set_stylesheet (ml_window, const char *stylesheet);
+extern const char *ml_window_get_stylesheet (ml_window);
+extern void ml_window_set_charset (ml_window, const char *charset);
+extern const char *ml_window_get_charset (ml_window);
+extern void ml_window_set_refresh (ml_window, int refresh);
+extern int ml_window_get_refresh (ml_window);
+extern void ml_window_scroll_to (ml_window, int x, int y);
+extern ml_window new_ml_frameset (struct ml_session *, pool pool, const char *rows, const char *cols, vector frames);
+extern void ml_frameset_set_description (ml_window, struct ml_session *, const char *rows, const char *cols, vector frames);
+extern void ml_frameset_set_title (ml_window, const char *);
+extern const char *ml_frameset_get_title (ml_window);
+extern ml_window new_ml_redirect (struct ml_session *, pool pool, const char *uri);
+extern void ml_redirect_set_uri (ml_window, const char *uri);
+extern const char *ml_redirect_get_uri (ml_window);
+
+/* Internal functions to repaint the window. */
+extern void _ml_window_notify_begin_response (ml_window w);
+extern int _ml_window_get_response_code (ml_window w);
+extern const char *_ml_window_get_response_name (ml_window w);
+extern void _ml_window_set_cookie_with_javascript (ml_window w, const char *cookie);
+extern void _ml_window_send_headers (ml_window w, pool thread_pool, http_response http_response);
+extern void _ml_window_repaint (ml_window, struct ml_session *, io_handle);
+
+/* Internal function to get the current windowid - used in a very few,
+ * quite rare places in monolith widgets.
+ */
+extern const char *_ml_window_get_windowid (ml_window);
+
+#endif /* ML_WINDOW_H */
diff --git a/src/monolith.c b/src/monolith.c
new file mode 100644 (file)
index 0000000..faa57b4
--- /dev/null
@@ -0,0 +1,1053 @@
+/* Monolith core code.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: monolith.c,v 1.27 2003/04/30 13:15:35 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#include <pool.h>
+#include <hash.h>
+#include <vector.h>
+#include <pstring.h>
+#include <pthr_reactor.h>
+#include <pthr_pseudothread.h>
+#include <pthr_iolib.h>
+#include <pthr_mutex.h>
+#include <pthr_http.h>
+#include <pthr_wait_queue.h>
+#include <rws_request.h>
+
+#include "ml_window.h"
+#include "monolith.h"
+
+#ifndef STRINGIFY
+#ifdef HAVE_STRINGIZE
+#define STRINGIFY(STRING) #STRING
+#else
+#define STRINGIFY(STRING) "STRING"
+#endif
+#endif /* ! STRINGIFY */
+
+/* Define some RFC-compliant dates to represent past and future. */
+#define DISTANT_PAST   "Thu, 01 Dec 1994 16:00:00 GMT"
+#define DISTANT_FUTURE "Sun, 01 Dec 2030 16:00:00 GMT"
+
+/* Headers which are sent to defeat caches. */
+#define NO_CACHE_HEADERS "Cache-Control", "must-revalidate", \
+                         "Expires", DISTANT_PAST, \
+                         "Pragma", "no-cache"
+
+/* These are the default session reaping parameters, all in seconds. */
+#define SESSION_REAP_MIN 600
+#define SESSION_REAP_MAX 3600
+#define SESSION_REAP_INC 600
+
+struct ml_session
+{
+  const char *sessionid;       /* Session ID (string of 32 hex digits). */
+  pool session_pool;           /* Pool for this session. */
+  mutex lock;                  /* Lock for this session. */
+  int hits;                    /* Number of requests in this session. */
+  reactor_time_t created, last_access; /* Session created, last accessed. */
+  int reap_min;                        /* Time to reap, if hits == 1. */
+  int reap_max;                        /* Maximum reap time. */
+  int reap_inc;                        /* Increment in reap time, per hit. */
+  struct sockaddr_in original_ip; /* IP address of initial request. */
+  cgi args;                    /* Initial arguments. */
+  cgi submitted_args;          /* Current arguments (short-lived). */
+  rws_request rws_rq;          /* Current request (short-lived). */
+  io_handle io;                        /* Current IO handle (short-lived). */
+  void (*app_main) (ml_session); /* Main entry point into the application. */
+  ml_window current_window;    /* "Current" window for the application. */
+  ml_window main_window;       /* Nominated main window for the application.*/
+  shash windows;               /* Maps window IDs -> ml_window. */
+  shash actions;               /* Maps action IDs -> callback functions. */
+  const char *host_header;     /* Host header. */
+  const char *canonical_path;  /* Full path to the script. */
+  const char *script_name;     /* Just the name of the script. */
+  const char *user_agent;      /* User agent. */
+  hash dbhs;                   /* Hash db_handle -> pools of
+                                * handles given out in current session. */
+  int userid;                  /* Currently logged in user (0 = none). */
+  const char *auth_cookie;      /* If set, send an auth cookie at the end
+                                * of the current HTTP request. */
+  const char *auth_cookie_path, *auth_cookie_expires;
+};
+
+struct action
+{
+  void (*callback_fn) (ml_session, void *data);
+  void *data;
+};
+
+static pool ml_pool;           /* Monolith library's own pool. */
+static shash sessions;         /* Maps session IDs -> ml_session. */
+static const char *auth_dbf;   /* Connection used for authentication. */
+
+static void run_action (ml_session, const char *);
+static int bad_request_error (rws_request rq, const char *text);
+static int auth_to_userid (ml_session, const char *auth);
+static void monolith_init (void) __attribute__ ((constructor));
+static void monolith_stop (void) __attribute__ ((destructor));
+static void kill_session (const char *sessionid);
+
+static void
+monolith_init ()
+{
+  ml_pool = new_subpool (global_pool);
+  sessions = new_shash (ml_pool, ml_session);
+}
+
+static void
+monolith_stop ()
+{
+  /* Note that this will also free up memory used by sessions, since
+   * each session pool is a subpool of ml_pool.
+   */
+  delete_pool (ml_pool);
+}
+
+static inline void
+kill_old_sessions ()
+{
+  static reactor_time_t last_reap = 0;
+
+  /* Only reap sessions every 10s. This also ensures that only one thread
+   * will try to kill sessions.
+   */
+  if (reactor_time - last_reap > 10000LL)
+    {
+      vector session_list;
+      int i;
+
+      last_reap = reactor_time;
+
+      session_list
+       = shash_values_in_pool (sessions, pth_get_pool (current_pth));
+
+      for (i = 0; i < vector_size (session_list); ++i)
+       {
+         ml_session session;
+         int reap_age;
+
+         vector_get (session_list, i, session);
+
+         /* Calculate the age before reaping. */
+         reap_age = session->reap_min +
+           (session->hits - 1) * session->reap_inc;
+         if (reap_age > session->reap_max)
+           reap_age = session->reap_max;
+
+         /* Session is older than this? */
+         if (reactor_time - session->last_access > reap_age * 1000LL)
+           {
+#if 0
+             fprintf (stderr,
+                      "reaping session ID %s\n"
+                      "current time = %llu, last access = %llu, diff = %llu",
+                      session->sessionid,
+                      reactor_time,
+                      session->last_access,
+                      reactor_time - session->last_access
+                      );
+#endif
+             kill_session (session->sessionid);
+           }
+       }
+    }
+}
+
+static inline int
+my_isxdigit (char c)
+{
+  return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
+}
+
+static inline const char *
+get_sessionid_from_cookie (http_request http_request)
+{
+  const char *sessionid;
+  int i;
+
+  sessionid = http_request_get_cookie (http_request, "ml_sessionid");
+  if (sessionid)
+    {
+      /* Check that the cookie has a valid form (32 hex digits). If
+       * not, just ignore it.
+       */
+      if (strlen (sessionid) == 32)
+       {
+         for (i = 0; i < 32; ++i)
+           if (!my_isxdigit (sessionid[i]))
+             break;
+         return sessionid;
+       }
+    }
+
+  return 0;
+}
+
+/* This function is so useful, it should be part of c2lib (XXX)
+ * See also the other occurrence in ml_login_nopw.c
+ */
+static inline const char *
+generate_sessionid (pool pool)
+{
+  int fd, i;
+  unsigned char buffer[16];
+  char *sessionid = pmalloc (pool, 33 * sizeof (char));
+
+  fd = open ("/dev/urandom", O_RDONLY);
+  if (fd == -1) abort ();
+  if (read (fd, buffer, 16) != 16) abort ();
+  close (fd);
+
+  for (i = 0; i < 16; ++i)
+    sprintf (sessionid + i*2, "%02x", buffer[i]);
+
+  return sessionid;
+}
+
+static const char *
+get_script_name (pool pool, const char *canonical_path)
+{
+  char *t = strrchr (canonical_path, '/');
+  return t ? pstrdup (pool, t+1) : canonical_path;
+}
+
+int
+ml_entry_point (rws_request rq, void (*app_main) (ml_session))
+{
+  pool thread_pool = pth_get_pool (current_pth);
+  io_handle io = rws_request_io (rq);
+  http_request http_request = rws_request_http_request (rq);
+  const char *host_header = rws_request_host_header (rq);
+  const char *canonical_path = rws_request_canonical_path (rq);
+  const char *sessionid;
+  cgi cgi;
+  ml_session session;
+  int send_sessionid = 0;
+  http_response http_response;
+  int close;
+  const char *actionid, *windowid, *auth;
+
+  /* Look for old sessions and kill them. */
+  kill_old_sessions ();
+
+  /* Get the sessionid, if there is one. */
+  sessionid = get_sessionid_from_cookie (http_request);
+
+  /* Parse the CGI parameters, and extract the monolith-specific
+   * parameters. Note that these are parsed into the thread pool,
+   * not into the session pool (we don't even know yet if we have
+   * a session pool, so that's not an option).
+   */
+  cgi = new_cgi (thread_pool, http_request, io);
+
+  /* If ml_reset passed, begin a new session regardless. */
+  if (cgi_param (cgi, "ml_reset"))
+    {
+      /* But if there was an existing session, delete it now. */
+      if (sessionid)
+       kill_session (sessionid);
+
+      sessionid = 0;
+    }
+
+  if (sessionid && shash_get (sessions, sessionid, session))
+    {
+      /* It's an existing, valid session. */
+
+      /* Acquire the lock before accessing any parts of the session
+       * structure.
+       */
+      mutex_enter (session->lock);
+
+      /* Update the access time. */
+      session->last_access = reactor_time;
+
+      /* Hit. */
+      session->hits++;
+
+      /* Get the current window, from the ml_window parameter. If there
+       * is no ml_window parameter (can happen when opening new windows),
+       * then set current window to NULL and expect that the action will
+       * set the current window.
+       */
+      session->current_window = session->main_window;
+      windowid = cgi_param (cgi, "ml_window");
+      if (windowid &&
+         ! shash_get (session->windows, windowid, session->current_window))
+       {
+         return bad_request_error (rq,
+                                   psprintf (thread_pool,
+                                             "invalid window ID: %s",
+                                             windowid));
+       }
+
+      /* Set the rws_rq field to the current request. */
+      session->rws_rq = rq;
+
+      /* Set the current IO handle. */
+      session->io = io;
+
+      /* Get ready for sending back auth cookie. */
+      session->auth_cookie = 0;
+
+      /* If the userid is set, check to see if there is a "poison" cookie.
+       * If so, then we log out the user.
+       */
+      if (session->userid != 0 &&
+         (auth = http_request_get_cookie (http_request, "ml_auth")) != 0
+         && strcmp (auth, "poison") == 0)
+       {
+         session->userid = 0;
+       }
+      else if (session->userid == 0 &&
+              (auth = http_request_get_cookie (http_request, "ml_auth")) != 0)
+       {
+         session->userid = auth_to_userid (session, auth);
+       }
+
+      /* If the ml_action parameter is given, invoke the appropriate
+       * function.
+       */
+      actionid = cgi_param (cgi, "ml_action");
+
+      /* Save the submitted args, for forms. */
+      session->submitted_args = cgi;
+
+      if (actionid)
+       run_action (session, actionid);
+    }
+  else
+    {
+      /* Start a new session. */
+      pool session_pool;
+      socklen_t namelen;
+      const char *ua;
+
+      /* Create a pool for this session. */
+      session_pool = new_subpool (ml_pool);
+
+      /* Create some state for this session. */
+      session = pmalloc (session_pool, sizeof *session);
+      session->lock = new_mutex (session_pool);
+      session->hits = 1;
+      session->last_access = session->created = reactor_time;
+      session->reap_min = SESSION_REAP_MIN;
+      session->reap_max = SESSION_REAP_MAX;
+      session->reap_inc = SESSION_REAP_INC;
+      session->session_pool = session_pool;
+      session->app_main = app_main;
+      session->current_window = 0;
+      session->main_window = 0;
+      session->windows = new_shash (session_pool, ml_window);
+      session->sessionid = sessionid = generate_sessionid (session_pool);
+      session->actions = new_shash (session_pool, struct action);
+      session->host_header = pstrdup (session_pool, host_header);
+      session->canonical_path = pstrdup (session_pool, canonical_path);
+      session->script_name =
+       get_script_name (session_pool, session->canonical_path);
+
+      /* Get the User-Agent header (if any). */
+      ua = http_request_get_header (http_request, "User-Agent");
+      session->user_agent = ua ? pstrdup (session_pool, ua) : 0;
+
+      /* Get the IP address of the first request. */
+      namelen = sizeof (session->original_ip);
+      getpeername (io_fileno (io),
+                  (struct sockaddr *) &session->original_ip, &namelen);
+
+      /* Take the initial arguments and copy them into the
+       * session pool, dropping the private ml_* parameters.
+       */
+      session->args = copy_cgi (session_pool, cgi);
+      cgi_erase (session->args, "ml_reset");
+      cgi_erase (session->args, "ml_window");
+      cgi_erase (session->args, "ml_action");
+
+      /* Set the rws_rq field to the current request. */
+      session->rws_rq = rq;
+
+      /* Set the current IO handle. */
+      session->io = io;
+
+      /* Initialize the list of database handles. */
+      session->dbhs = new_hash (session_pool, db_handle, pool);
+
+      /* See if there's an ml_auth cookie. If so, and it's valid, then
+       * we initialize the session->userid from this. Otherwise we
+       * set session->userid to 0.
+       */
+      if ((auth = http_request_get_cookie (http_request, "ml_auth")) != 0)
+       session->userid = auth_to_userid (session, auth);
+      else
+       session->userid = 0;
+
+      /* Get ready for sending back auth cookie. */
+      session->auth_cookie = 0;
+
+      /* Remember to send back the ml_sessionid cookie. */
+      send_sessionid = 1;
+
+      /* Save the session. */
+      shash_insert (sessions, sessionid, session);
+
+      /* Acquire the lock. (Actually we don't strictly need to do this
+       * until after we have sent the cookie, but it makes the code
+       * simpler).
+       */
+      mutex_enter (session->lock);
+
+      /* Run the "main" program. */
+      app_main (session);
+    }
+
+  if (! session->current_window)
+    {
+      return bad_request_error (rq, "no current window");
+    }
+
+  /* Begin the response. */
+  _ml_window_notify_begin_response (session->current_window);
+
+  http_response = new_http_response
+    (thread_pool, http_request, io,
+     _ml_window_get_response_code (session->current_window),
+     _ml_window_get_response_name (session->current_window));
+
+  http_response_send_headers (http_response,
+                             /* Send headers to defeat caching. */
+                             NO_CACHE_HEADERS,
+                             /* End of headers. */
+                             NULL);
+
+  /* Send the session cookie if necessary. */
+  if (send_sessionid)
+    {
+      const char *cookie = psprintf (thread_pool,
+                                    "ml_sessionid=%s; path=%s",
+                                    sessionid,
+                                    canonical_path);
+
+      http_response_send_header (http_response, "Set-Cookie", cookie);
+      _ml_window_set_cookie_with_javascript (session->current_window, cookie);
+    }
+
+  /* Send the auth cookie if necessary. */
+  if (session->auth_cookie)
+    {
+      const char *cookie = psprintf (thread_pool,
+                                    "ml_auth=%s; path=%s; expires=%s",
+                                    session->auth_cookie,
+                                    session->auth_cookie_path
+                                    ? : canonical_path,
+                                    session->auth_cookie_expires
+                                    ? : "");
+
+      http_response_send_header (http_response, "Set-Cookie", cookie);
+      _ml_window_set_cookie_with_javascript (session->current_window, cookie);
+
+      session->auth_cookie = 0;
+    }
+
+  /* Send any additional headers required by the current window. */
+  _ml_window_send_headers (session->current_window, thread_pool,
+                          http_response);
+
+  close = http_response_end_headers (http_response);
+
+  if (!http_request_is_HEAD (http_request))
+    {
+      /* Display the main window. */
+      _ml_window_repaint (session->current_window, session, io);
+    }
+
+  /* XXX We might need to recover database handles here, particularly
+   * if we implement chunked encoding, and we start to process many
+   * requests over the same connection.
+   */
+
+  /* Free the session lock. */
+  mutex_leave (session->lock);
+
+  return close;
+}
+
+/* Delete a session.
+ *
+ * Killing a session is a non-trivial task, because we must make sure
+ * at all costs that no other thread is using the session structure, or
+ * might be waiting on the mutex to enter the session.
+ *
+ * The procedure is as follows. If any step fails, then we need to go
+ * back round to the top and try again. Eventually this function will
+ * delete the session.
+ *
+ * - Acquire the mutex.
+ * - Check if any other threads are waiting to enter the mutex.
+ * - If none, then remove the session from the sessions hash (this ensures
+ *   that no other thread will try to use the session - particularly
+ *   important if the session deletion is protracted and involves I/O).
+ * - Release the mutex (no other thread will try to acquire it).
+ * - Delete the session pool, which invokes any session finalisers.
+ */
+static void
+kill_session (const char *sessionid)
+{
+  ml_session session;
+
+  /* Get the session structure. */
+  if (!shash_get (sessions, sessionid, session))
+    return;                    /* No such session - ignore it. */
+
+ again:
+  /* Acquire the session lock. */
+  mutex_enter (session->lock);
+
+  /* Any other threads waiting to enter the mutex? */
+  if (mutex_nr_sleepers (session->lock) > 0)
+    {
+      /* Release the lock and try again later. */
+      mutex_leave (session->lock);
+      pth_millisleep (100);    /* To be on the safe side ... */
+      goto again;
+    }
+
+  /* Remove the session from the list of sessions. After this, no
+   * other threads can find or enter this session.
+   */
+  assert (shash_erase (sessions, sessionid));
+
+  /* Release the lock. */
+  mutex_leave (session->lock);
+
+  /* Finally, we can delete the thread. */
+  delete_pool (session->session_pool);
+}
+
+pool
+ml_session_pool (ml_session session)
+{
+  return session->session_pool;
+}
+
+cgi
+ml_session_args (ml_session session)
+{
+  return session->args;
+}
+
+const char *
+ml_session_sessionid (ml_session session)
+{
+  return session->sessionid;
+}
+
+const char *
+ml_session_host_header (ml_session session)
+{
+  return session->host_header;
+}
+
+const char *
+ml_session_canonical_path (ml_session session)
+{
+  return session->canonical_path;
+}
+
+const char *
+ml_session_script_name (ml_session session)
+{
+  return session->script_name;
+}
+
+const char *
+ml_session_user_agent (ml_session session)
+{
+  return session->user_agent;
+}
+
+void
+ml_session_set_main_window (ml_session session, ml_window win)
+{
+  session->main_window = win;
+}
+
+ml_window
+ml_session_get_main_window (ml_session session)
+{
+  return session->main_window;
+}
+
+int
+ml_session_get_peername (ml_session session,
+                        struct sockaddr *name, socklen_t *namelen)
+{
+  int s;
+
+  s = io_fileno (session->io);
+  return getpeername (s, name, namelen);
+}
+
+const char *
+ml_session_get_peernamestr (ml_session session)
+{
+  struct sockaddr_in addr;
+  socklen_t len = sizeof (addr);
+
+  if (ml_session_get_peername (session, (struct sockaddr *) &addr, &len) == -1)
+    {
+      perror ("getpeername");
+      return 0;
+    }
+
+  assert (addr.sin_family == AF_INET);
+  return pstrdup (session->session_pool, inet_ntoa (addr.sin_addr));
+}
+
+const vector
+_ml_get_sessions (pool pool)
+{
+  return shash_keys_in_pool (sessions, pool);
+}
+
+ml_session
+_ml_get_session (const char *sessionid)
+{
+  ml_session session;
+
+  if (shash_get (sessions, sessionid, session))
+    return session;
+  else
+    return 0;
+}
+
+int
+_ml_session_get_hits (ml_session session)
+{
+  return session->hits;
+}
+
+reactor_time_t
+_ml_session_get_last_access (ml_session session)
+{
+  return session->last_access;
+}
+
+reactor_time_t
+_ml_session_get_created (ml_session session)
+{
+  return session->created;
+}
+
+struct sockaddr_in
+_ml_session_get_original_ip (ml_session session)
+{
+  return session->original_ip;
+}
+
+void *
+_ml_session_get_app_main (ml_session session)
+{
+  return session->app_main;
+}
+
+const vector
+_ml_session_get_windows (ml_session session, pool pool)
+{
+  return shash_keys_in_pool (session->windows, pool);
+}
+
+ml_window
+_ml_session_get_window (ml_session session, const char *windowid)
+{
+  ml_window win;
+
+  if (shash_get (session->windows, windowid, win))
+    return win;
+  else
+    return 0;
+}
+
+const vector
+_ml_session_get_actions (ml_session session, pool pool)
+{
+  return shash_keys_in_pool (session->actions, pool);
+}
+
+int
+_ml_session_get_action (ml_session session, const char *actionid,
+                       void **fn_rtn, void **data_rtn)
+{
+  struct action action;
+
+  if (shash_get (session->actions, actionid, action))
+    {
+      *fn_rtn = action.callback_fn;
+      *data_rtn = action.data;
+      return 1;
+    }
+  else
+    return 0;
+}
+
+const vector
+_ml_session_get_dbhs (ml_session session, pool pool)
+{
+  return hash_keys_in_pool (session->dbhs, pool);
+}
+
+void
+ml_session_release_lock (ml_session session)
+{
+  mutex_leave (session->lock);
+}
+
+void
+ml_session_acquire_lock (ml_session session)
+{
+  mutex_enter (session->lock);
+
+  /* We've probably been sleeping for a while, so update the last access
+   * time to reflect this.
+   */
+  session->last_access = reactor_time;
+}
+
+cgi
+_ml_session_submitted_args (ml_session session)
+{
+  return session->submitted_args;
+}
+
+static void get_auth_dbf (ml_session);
+static const char *parse_expires_header (pool pool, const char *expires);
+
+void
+ml_session_login (ml_session session, int userid,
+                 const char *path, const char *expires)
+{
+  pool thread_pool = pth_get_pool (current_pth);
+  db_handle dbh;
+  st_handle sth;
+  const char *cookie;
+
+  /* Parse the expires header. */
+  expires = parse_expires_header (thread_pool, expires);
+
+  get_auth_dbf (session);
+  dbh = get_db_handle (auth_dbf, DBI_THROW_ERRORS);
+
+  /* Generate a suitable cookie and insert it into the database. */
+  cookie = generate_sessionid (thread_pool);
+  sth = st_prepare_cached
+    (dbh,
+     "delete from ml_user_cookie where userid = ?",
+     DBI_INT);
+  st_execute (sth, userid);
+  sth = st_prepare_cached
+    (dbh,
+     "insert into ml_user_cookie (userid, cookie) values (?, ?)",
+     DBI_INT, DBI_STRING);
+  st_execute (sth, userid, cookie);
+  db_commit (dbh);
+  put_db_handle (dbh);
+
+  /* User is logged in. */
+  session->userid = userid;
+
+  /* Remember to send back a cookie. */
+  session->auth_cookie = cookie;
+  session->auth_cookie_path = path;
+  session->auth_cookie_expires = expires;
+}
+
+void
+ml_session_logout (ml_session session, const char *path)
+{
+  pool thread_pool = pth_get_pool (current_pth);
+  db_handle dbh;
+  st_handle sth;
+  int old_userid = session->userid;
+  const char *expires;
+
+  /* Set the expires header. */
+  expires = parse_expires_header (thread_pool, "+1y");
+
+  /* If no one is actually logged in, do nothing. */
+  if (!old_userid) return;
+
+  get_auth_dbf (session);
+  dbh = get_db_handle (auth_dbf, DBI_THROW_ERRORS);
+
+  sth = st_prepare_cached
+    (dbh,
+     "delete from ml_user_cookie where userid = ?",
+     DBI_INT);
+  st_execute (sth, old_userid);
+  db_commit (dbh);
+  put_db_handle (dbh);
+
+  /* User is logged out. */
+  session->userid = 0;
+
+  /* Remember to send back the poison cookie. */
+  session->auth_cookie = "poison";
+  session->auth_cookie_path = path;
+  session->auth_cookie_expires = expires;
+}
+
+/* Convert auth cookie to userid, if possible. If not valid, returns 0. */
+static int
+auth_to_userid (ml_session session, const char *auth)
+{
+  db_handle dbh;
+  st_handle sth;
+  int userid, fetched;
+
+  get_auth_dbf (session);
+  dbh = get_db_handle (auth_dbf, DBI_THROW_ERRORS);
+
+  sth = st_prepare_cached
+    (dbh,
+     "select userid from ml_user_cookie where cookie = ?",
+     DBI_STRING);
+  st_execute (sth, auth);
+
+  st_bind (sth, 0, userid, DBI_INT);
+
+  fetched = st_fetch (sth);
+
+  put_db_handle (dbh);
+
+  if (fetched)
+    return userid;
+  else
+    return 0;
+}
+
+static void
+get_auth_dbf (ml_session session)
+{
+  /* Check monolith is configured to handle user authentication. */
+  if (!auth_dbf)
+    {
+      auth_dbf = ml_cfg_get_string (session, "monolith user database", 0);
+      if (!auth_dbf)
+       pth_die ("missing 'monolith user database' key "
+                "in the rws configuration file");
+    }
+}
+
+/* XXX We should share this code with rws. */
+static const char *
+parse_expires_header (pool pool, const char *expires)
+{
+  char pm, unit;
+  int length;
+
+  if (expires == 0) return 0;
+
+  /* Is it like "+1y"? */
+  if (sscanf (expires, "%c%d%c", &pm, &length, &unit) == 3 &&
+      (pm == '+' || pm == '-') &&
+      length > 0 &&
+      (unit == 's' || unit == 'm' || unit == 'h' ||
+       unit == 'd' || unit == 'y'))
+    {
+      time_t t;
+      struct tm *tm;
+      char header[64];
+
+      time (&t);
+
+      if (pm == '+')
+       {
+         switch (unit)
+           {
+           case 's': t += length; break;
+           case 'm': t += length * 60; break;
+           case 'h': t += length * (60 * 60); break;
+           case 'd': t += length * (60 * 60 * 24); break;
+           case 'y': t += length * (60 * 60 * 24 * 366); break;
+           }
+       }
+      else
+       {
+         switch (unit)
+           {
+           case 's': t -= length; break;
+           case 'm': t -= length * 60; break;
+           case 'h': t -= length * (60 * 60); break;
+           case 'd': t -= length * (60 * 60 * 24); break;
+           case 'y': t -= length * (60 * 60 * 24 * 366); break;
+           }
+       }
+
+      tm = gmtime (&t);
+      strftime (header, sizeof header, "%a, %d %b %Y %H:%M:%S GMT", tm);
+
+      return pstrdup (pool, header);
+    }
+
+  /* Otherwise, assume it's in RFC 2616 format. */
+  return expires;
+}
+
+int
+ml_session_userid (ml_session session)
+{
+  return session->userid;
+}
+
+const char *
+ml_cfg_get_string (ml_session session,
+                  const char *key, const char *default_value)
+{
+  return rws_request_cfg_get_string (session->rws_rq, key, default_value);
+}
+
+int
+ml_cfg_get_int (ml_session session, const char *key, int default_value)
+{
+  return rws_request_cfg_get_int (session->rws_rq, key, default_value);
+}
+
+int
+ml_cfg_get_bool (ml_session session, const char *key, int default_value)
+{
+  return rws_request_cfg_get_bool (session->rws_rq, key, default_value);
+}
+
+void
+_ml_session_set_current_window (ml_session session, ml_window window,
+                               const char *windowid)
+{
+  shash_insert (session->windows, windowid, window);
+  session->current_window = window;
+}
+
+static void
+run_action (ml_session session, const char *action_id)
+{
+  struct action a;
+
+  /* Ignore unknown action IDs. */
+  if (shash_get (session->actions, action_id, a))
+    a.callback_fn (session, a.data);
+}
+
+const char *
+ml_register_action (ml_session session,
+                   void (*callback_fn) (ml_session, void *data), void *data)
+{
+  static int action_id = 1;
+  const char *action_str;
+  struct action a;
+
+  action_str = pitoa (session->session_pool, action_id);
+  action_id++;
+
+  a.callback_fn = callback_fn;
+  a.data = data;
+
+  shash_insert (session->actions, action_str, a);
+
+  return action_str;
+}
+
+void
+ml_unregister_action (ml_session session, const char *action_id)
+{
+  shash_erase (session->actions, action_id);
+}
+
+#define CRLF "\015\012"
+
+static int
+bad_request_error (rws_request rq, const char *text)
+{
+  pool thread_pool = pth_get_pool (current_pth);
+  io_handle io = rws_request_io (rq);
+  http_request http_request = rws_request_http_request (rq);
+  const char *canonical_path = rws_request_canonical_path (rq);
+  http_response http_response;
+  int close;
+
+  http_response = new_http_response (thread_pool, http_request, io,
+                                    500, "Internal server error");
+  http_response_send_headers (http_response,
+                             /* Content type. */
+                             "Content-Type", "text/html",
+                             NO_CACHE_HEADERS,
+                             /* End of headers. */
+                             NULL);
+  close = http_response_end_headers (http_response);
+
+  if (http_request_is_HEAD (http_request)) return close;
+
+  io_fprintf (io,
+             "<html><head><title>Internal server error</title></head>" CRLF
+             "<body bgcolor=\"#ffffff\">" CRLF
+             "<h1>500 Internal server error</h1>" CRLF
+             "<p>There was an error serving this request: %s</p>" CRLF
+             "<hr>" CRLF
+             "<p><a href=\"%s?ml_reset=1\">Restart your session</a></p>" CRLF
+             "</body></html>",
+             text, canonical_path);
+
+  return close;
+}
diff --git a/src/monolith.h b/src/monolith.h
new file mode 100644 (file)
index 0000000..5a52c49
--- /dev/null
@@ -0,0 +1,339 @@
+/* Monolith core code.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: monolith.h,v 1.19 2003/02/22 15:34:32 rich Exp $
+ */
+
+#ifndef MONOLITH_H
+#define MONOLITH_H
+
+#include <pool.h>
+#include <vector.h>
+#include <pthr_pseudothread.h>
+#include <pthr_cgi.h>
+#include <pthr_dbi.h>
+#include <rws_request.h>
+
+#include <stdlib.h>            /* For size_t */
+
+/* Just like the semi-standard "offsetof" function. */
+#ifdef offsetof
+#define ml_offsetof(type,member) offset(type,member)
+#else
+#define ml_offsetof(type,member) ((size_t) &((type *)0)->member)
+#endif
+
+#include <sys/socket.h>
+
+#include <ml_window.h>
+
+/* Session object. */
+struct ml_session;
+typedef struct ml_session *ml_session;
+
+/* Function: ml_entry_point - entry point into monolith from handle_request
+ *
+ * @code{ml_entry_point} is the entry point into the monolith core
+ * library code, called from @code{handle_request}. The application's
+ * @code{handle_request} function should contain just a single
+ * call to @code{ml_entry_point} like this:
+ *
+ * @code{return ml_entry_point (rq, app_main);}
+ *
+ * where @code{rq} is the @code{rws_request} object passed by the
+ * web server, and @code{app_main} is the application's main function.
+ *
+ * See @code{examples/01_label_and_button.c} for a simple monolith
+ * application.
+ *
+ * See also: @ref{ml_session_pool(3)},
+ * @ref{rws_request_pool(3)}, @ref{new_ml_window(3)},
+ * @ref{ml_cfg_get_string(3)}.
+ */
+extern int ml_entry_point (rws_request rq, void (*app_main) (ml_session));
+
+/* Function: ml_session_pool - get monolith per-session information
+ * Function: ml_session_args
+ * Function: ml_session_sessionid
+ * Function: ml_session_host_header
+ * Function: ml_session_canonical_path
+ * Function: ml_session_script_name
+ * Function: ml_session_user_agent
+ * Function: ml_session_set_main_window
+ * Function: ml_session_get_main_window
+ * Function: ml_session_get_peername
+ * Function: ml_session_get_peernamestr
+ *
+ * These functions extract the information from the opaque @code{ml_session}
+ * object passed to most application functions and callbacks. Each separate
+ * user session is tied to a separate @code{ml_session} object which
+ * contains standard fields.
+ *
+ * @code{ml_session_pool} returns the session pool, which is a pool which
+ * has the lifetime of the whole session.
+ *
+ * @code{ml_session_args} returns the arguments (a @code{cgi} object) which
+ * were passed to the application when it started running. This is kind of
+ * equivalent to @code{argc} and @code{argv} for a traditional application.
+ *
+ * @code{ml_session_sessionid} returns the session ID, a 32 character
+ * string of random hex digits which also happens to be the cookie
+ * passed by the browser.
+ *
+ * @code{ml_session_host_header} returns the Host: header of the
+ * monolith application. For example @code{www.annexia.org}.
+ *
+ * @code{ml_session_canonical_path} returns the canonical path of the
+ * monolith application, that is, the full path of the application as
+ * it appears to the browser. For example @code{/so-bin/app.so}.
+ *
+ * @code{ml_session_script_name} returns just the name of the application
+ * as it appears to the browser. For example @code{app.so}.
+ *
+ * @code{ml_session_user_agent} returns the identifying string for
+ * the browser, sent in the HTTP @code{User-Agent} header. This can
+ * be @code{NULL}.
+ *
+ * @code{ml_session_(set|get)_main_window} are a pair of esoteric
+ * functions which you will not need to use in most applications. They
+ * are useful, however, when you are building a website. They nominate
+ * a particular window which will be the window displayed if the user
+ * types the URL of the application directly into their browser
+ * location bar, without the normally mandatory @code{ml_window}
+ * parameter.
+ *
+ * @code{ml_session_get_peername} returns the peer address of the
+ * socket. The peer address is the IP address of the user's browser
+ * or proxy. This function returns @code{0} on success or @code{-1}
+ * on failure (with the appropriate error code in @code{errno}).
+ * @code{ml_session_get_peernamestr} returns the same
+ * converted to a string, normally in "dotted quad" format, for example:
+ * @code{"10.0.0.137"}. On failure, @code{ml_session_get_peernamestr}
+ * returns @code{NULL}.
+ *
+ * See also: @ref{ml_entry_point(3)}, @ref{ml_die(3)}, @ref{new_cgi(3)}
+ */
+extern pool ml_session_pool (ml_session);
+extern cgi ml_session_args (ml_session);
+extern const char *ml_session_sessionid (ml_session);
+extern const char *ml_session_host_header (ml_session);
+extern const char *ml_session_canonical_path (ml_session);
+extern const char *ml_session_script_name (ml_session);
+extern const char *ml_session_user_agent (ml_session);
+extern void ml_session_set_main_window (ml_session, ml_window);
+extern ml_window ml_session_get_main_window (ml_session);
+extern int ml_session_get_peername (ml_session, struct sockaddr *name, socklen_t *namelen);
+extern const char *ml_session_get_peernamestr (ml_session);
+
+/* Function: ml_session_release_lock
+ * Function: ml_session_acquire_lock
+ *
+ * These are advanced functions which you will probably never need to use.
+ *
+ * Monolith maintains a single session structure for each session. If
+ * the browser were to try and fetch two pages with the same session ID
+ * at the same time, then rws could create two threads running at the
+ * same time, and these two threads could access the same session
+ * structure. The result of this, under some circumstances, would be
+ * that the session structure would be corrupted. There is obviously
+ * potential for malicious abuse here, but this can also happen under
+ * normal conditions, particularly in framesets (the browser fetches
+ * each frame at the same time).
+ *
+ * To avoid this situation, monolith maintains a mutex lock on each
+ * session structure, so that no two threads can try to access the
+ * same session structure at the same time. (Different sessions can
+ * execute concurrently, of course).
+ *
+ * Monolith's session locking is normally transparent to programmers
+ * and users, but these functions allow you to bypass the session
+ * lock under certain circumstances. Of course, when a programmer
+ * does this, they can no longer rely on monolith to keep the session
+ * structure consistent, and must instead guarantee this themselves.
+ *
+ * @code{ml_session_release_lock} releases the session lock. This
+ * potentially allows other threads to run, overwriting parts of the
+ * session structure.
+ *
+ * @code{ml_session_acquire_lock} reacquires the session lock, possibly
+ * sleeping to do so. This does not restore the session structure which
+ * will still be in a semi-corrupted state.
+ *
+ * In future we will add functions to allow @code{ml_session_acquire_lock}
+ * to restore the session structure to a non-corrupt state, perhaps by
+ * having a stack of session structures (or some parts of the session
+ * structure).
+ *
+ * See also: @ref{new_pseudothread(3)}, @ref{new_mutex(3)}.
+ */
+extern void ml_session_release_lock (ml_session);
+extern void ml_session_acquire_lock (ml_session);
+
+/* Function: ml_session_login - user authentication, log in, log out
+ * Function: ml_session_logout
+ * Function: ml_session_userid
+ *
+ * These functions provide a low-level, database-independent,
+ * authentication-method-agnostic way to handle the problem
+ * of user authentication in monolith applications.
+ *
+ * All of these functions require the schema in
+ * @code{sql/monolith_auth_create.sql}. This schema contains a
+ * table which stores a mapping from cookies to user IDs. The
+ * @code{rws} configuration file must contain a line which allows
+ * monolith to find the database containing this table:
+ *
+ * @code{monolith user database: CONNINFO}
+ *
+ * (@code{CONNINFO} is the PostgreSQL connection info string).
+ *
+ * Each user must be identified by a unique numeric userid >= 1.
+ *
+ * Once a user has logged in, a cookie (@code{ml_auth}) is sent back
+ * to their browser. The reason for sending back a cookie, rather than
+ * just storing the userid in the session state, is that it might be
+ * necessary for the user to be automatically logged in to other
+ * applications on the same server. By sending the cookie back to,
+ * say, the @code{/} path, a user effectively logs into all
+ * applications whenever they log into any one application. Also,
+ * by controlling the expiry time of the cookie, it is possible
+ * to allow a user to remain logged in across several browser
+ * sessions.
+ *
+ * The session stores the currently logged in user (as a userid)
+ * or @code{0} if no user is currently logged in. @code{ml_session_userid}
+ * retrieves the currently logged in userid or @code{0}. Note that
+ * the currently logged in user can change unexpectedly, so you
+ * must NOT stash the userid away. Call @code{ml_session_userid}
+ * every time you want to check the currently logged in user. See
+ * below for more details.
+ *
+ * When a user logs in to a particular application, the application
+ * should call @code{ml_session_login}. The actual method of login
+ * is not defined at this level (but monolith provides some higher-level
+ * widgets which you may use). The @code{userid} parameter is the
+ * user who has logged in. The @code{path} and @code{expires} parameters
+ * are used to control the @code{ml_auth} cookie sent back to the
+ * browser. If @code{path} is @code{NULL}, then the cookie only applies
+ * to the current application. If @code{path} is @code{"/"}, then the
+ * user will be automatically logged into all applications running
+ * at the same address. Other @code{path}s may also be specified to
+ * restrict authentication to some subset of the path space. If
+ * @code{expires} is @code{NULL}, then the cookie will expire at
+ * the end of the current browser session (when the user closes their
+ * browser). This means that the next time they visit the site, they
+ * will need to log in again, if they have closed their browser in
+ * the meantime. @code{expires} may also be set to a date in
+ * RFC 2616 compliant format, or to a relative time such as
+ * @code{"+1y"} (meaning 1 year from now). Relative times are
+ * specified in exactly the same way as the @code{rws} @code{expires}
+ * configuration option.
+ *
+ * There are various browser-related problems which mean that you
+ * should not mix and match different @code{path}s unless you really
+ * know what you are doing. In general, you should always set @code{path}
+ * to either @code{"/"} everywhere, or to @code{NULL} everywhere. The
+ * simplest thing is to always set @code{path} to @code{"/"} everywhere.
+ *
+ * If @code{path} is not @code{NULL}, then calling @code{ml_session_login}
+ * may also cause other monolith applications to become logged in.
+ * This is because they notice the @code{ml_auth} cookie, verify it
+ * against the database, and if it is verified, change their state
+ * to logged in. For this reason, applications should not stash away
+ * the result of @code{ml_session_userid}, but should check it each
+ * time they need it, because it can change unexpectedly.
+ *
+ * @code{ml_session_logout} logs the current user out. @code{path} should
+ * be the same as above.
+ *
+ * As before, if @code{path} is not @code{NULL}, then calling
+ * @code{ml_session_logout} may cause other monolith applications to
+ * become logged out (@code{ml_session_userid} will start to return
+ * @code{0}). This is because monolith overwrites the @code{ml_auth}
+ * cookie with a "poisoned" cookie which other applications may notice.
+ */
+extern void ml_session_login (ml_session, int userid, const char *path, const char *expires);
+extern void ml_session_logout (ml_session, const char *path);
+extern int ml_session_userid (ml_session);
+
+/* Function: ml_cfg_get_string - get values from the configuration file
+ * Function: ml_cfg_get_int
+ * Function: ml_cfg_get_bool
+ *
+ * @code{rws_request_cfg_get_string} returns the configuration file
+ * string for @code{key}. If there is no entry in the configuration
+ * file, this returns @code{default_value}.
+ *
+ * @code{rws_request_cfg_get_int} returns the string converted to
+ * an integer.
+ *
+ * @code{rws_request_cfg_get_bool} returns the string converted to
+ * a boolean.
+ *
+ * See also: @ref{ml_entry_point(3)}, @ref{rws_request_cfg_get_string(3)}.
+ *
+ */
+extern const char *ml_cfg_get_string (ml_session, const char *key, const char *default_value);
+extern int ml_cfg_get_int (ml_session, const char *key, int default_value);
+extern int ml_cfg_get_bool (ml_session, const char *key, int default_value);
+
+/* Function: ml_register_action - register callbacks
+ * Function: ml_unregister_action
+ *
+ * Widgets such as buttons may register actions (callback functions)
+ * to be run when a user clicks a button or submits a form. Each
+ * action is associated with a unique action ID (a string). A separate
+ * set of actions is registered within each session. The callback
+ * function is invoked as @code{void callback_fn (ml_session
+ * session, void *data)}.
+ *
+ * @code{ml_register_action} registers an action within a given
+ * session and returns the action ID string.
+ *
+ * @code{ml_unregister_action} unregisters an action.
+ *
+ * See also: @ref{new_ml_button(3)}.
+ */
+extern const char *ml_register_action (ml_session session, void (*callback_fn) (ml_session, void *), void *data);
+extern void ml_unregister_action (ml_session session, const char *action_id);
+
+/* Private function used by forms to get the arguments passed just to
+ * this request. The returned value is on the short-lived thread pool,
+ * so interesting stuff must be copied to the session pool.
+ */
+extern cgi _ml_session_submitted_args (ml_session);
+
+/* Private function used by ml_window to register the current window. */
+extern void _ml_session_set_current_window (ml_session, ml_window, const char *windowid);
+
+/* Some private functions used by the stats package to inspect the internals
+ * of monolith. These functions are subject to change and should not be used
+ * in ordinary applications.
+ */
+extern const vector _ml_get_sessions (pool);
+extern ml_session _ml_get_session (const char *sessionid);
+extern int _ml_session_get_hits (ml_session);
+extern reactor_time_t _ml_session_get_last_access (ml_session);
+extern reactor_time_t _ml_session_get_created (ml_session);
+extern struct sockaddr_in _ml_session_get_original_ip (ml_session);
+extern void *_ml_session_get_app_main (ml_session);
+extern const vector _ml_session_get_windows (ml_session, pool);
+extern ml_window _ml_session_get_window (ml_session, const char *windowid);
+extern const vector _ml_session_get_actions (ml_session, pool);
+extern int _ml_session_get_action (ml_session, const char *actionid, void **fn_rtn, void **data_rtn);
+
+#endif /* MONOLITH_H */
diff --git a/src/text.c b/src/text.c
new file mode 100644 (file)
index 0000000..f52e2a9
--- /dev/null
@@ -0,0 +1,183 @@
+/* Monolith text processing functions.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: text.c,v 1.1 2002/12/08 17:29:17 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_smarttext.h"
+
+void
+ml_plaintext_print (io_handle io, const char *text)
+{
+  while (*text)
+    {
+      switch (*text)
+       {
+       default: io_fputc (*text, io); break;
+       case '<': io_fputs ("&lt;", io); break;
+       case '>': io_fputs ("&gt;", io); break;
+       case '\n': io_fputs ("<br>", io); break;
+       case '&': io_fputs ("&amp;", io); break;
+       case '"': io_fputs ("&quot;", io); break;
+       }
+      text++;
+    }
+}
+
+const char *
+ml_plaintext_to_html (pool pool, const char *text)
+{
+  char *new_text;
+  int len = strlen (text);
+  int new_len = 0;
+  int i, j;
+
+  /* Work out how long the escaped string will be. Escaped strings get
+   * bigger.
+   */
+  for (i = 0; i < len; ++i)
+    if (text[i] == '<' || text[i] == '>' || text[i] == '\n')
+      new_len += 4;
+    else if (text[i] == '&')
+      new_len += 5;
+    else if (text[i] == '"')
+      new_len += 6;
+    else
+      new_len++;
+
+  new_text = pmalloc (pool, sizeof (char) * (new_len+1));
+
+  /* Now copy and escape the string. */
+  for (i = j = 0; i < len; ++i)
+    switch (text[i])
+      {
+      default:
+       new_text[j++] = text[i];
+       break;
+      case '<':
+       new_text[j++] = '&';
+       new_text[j++] = 'l';
+       new_text[j++] = 't';
+       new_text[j++] = ';';
+       break;
+      case '>':
+       new_text[j++] = '&';
+       new_text[j++] = 'g';
+       new_text[j++] = 't';
+       new_text[j++] = ';';
+       break;
+      case '\n':
+       new_text[j++] = '<';
+       new_text[j++] = 'b';
+       new_text[j++] = 'r';
+       new_text[j++] = '>';
+       break;
+      case '&':
+       new_text[j++] = '&';
+       new_text[j++] = 'a';
+       new_text[j++] = 'm';
+       new_text[j++] = 'p';
+       new_text[j++] = ';';
+       break;
+      case '"':
+       new_text[j++] = '&';
+       new_text[j++] = 'q';
+       new_text[j++] = 'u';
+       new_text[j++] = 'o';
+       new_text[j++] = 't';
+       new_text[j++] = ';';
+       break;
+      }
+
+  new_text[j++] = '\0';
+
+  return new_text;
+}
+
+void
+ml_smarttext_print (io_handle io, const char *text)
+{
+  pool tmp = new_subpool (global_pool);
+  const char *s;
+
+  s = ml_smarttext_to_html (tmp, text);
+  io_fputs (s, io);
+  delete_pool (tmp);
+}
+
+void
+ml_filterhtml_print (io_handle io, const char *text)
+{
+  /* XXX NOT IMPLEMENTED XXX */
+  io_fputs (text, io);
+}
+
+const char *
+ml_filterhtml_to_html (pool pool, const char *text)
+{
+  /* XXX NOT IMPLEMENTED XXX */
+  return text;
+}
+
+void
+ml_anytext_print (io_handle io, const char *text, char type)
+{
+  switch (type)
+    {
+    case 'p':
+      ml_plaintext_print (io, text);
+      break;
+    case 's':
+      ml_smarttext_print (io, text);
+      break;
+    case 'h':
+      ml_filterhtml_print (io, text);
+      break;
+    }
+}
+
+const char *
+ml_anytext_to_html (pool pool, const char *text, char type)
+{
+  switch (type)
+    {
+    case 'p':
+      text = ml_plaintext_to_html (pool, text);
+      break;
+    case 's':
+      text = ml_smarttext_to_html (pool, text);
+      break;
+    case 'h':
+      text = ml_filterhtml_to_html (pool, text);
+      break;
+    }
+
+  return text;
+}
diff --git a/widgets/ml_bulletins.c b/widgets/ml_bulletins.c
new file mode 100644 (file)
index 0000000..18f23aa
--- /dev/null
@@ -0,0 +1,485 @@
+/* Monolith bulletins (recent news spool).
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_bulletins.c,v 1.12 2003/02/22 15:34:32 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+#include <pre.h>
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_button.h"
+#include "ml_window.h"
+#include "ml_table_layout.h"
+#include "ml_form_layout.h"
+#include "ml_text_label.h"
+#include "ml_dialog.h"
+#include "ml_form.h"
+#include "ml_form_input.h"
+#include "ml_form_textarea.h"
+#include "ml_form_select.h"
+#include "ml_form_text.h"
+#include "ml_form_submit.h"
+#include "ml_bulletins.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations bulletins_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_bulletins
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_session session;          /* Current session. */
+  const char *conninfo;                /* Database connection. */
+  int sectionid;               /* Which section? */
+  int first_item;              /* First item to display. */
+  int nr_items;                        /* Number of items to display on each page. */
+  ml_button post, home, prev, next; /* Buttons along the bottom. */
+
+  /* These are used during posting. */
+  ml_form_textarea post_item;
+  ml_form_select post_type;
+  ml_form_text post_link;
+  ml_form_text post_link_text;
+};
+
+static int can_post (db_handle dbh, int sectionid, int userid);
+static void post (ml_session, void *vw);
+static void update_buttons (ml_bulletins w);
+static void home_button (ml_session, void *vw);
+static void prev_button (ml_session, void *vw);
+static void next_button (ml_session, void *vw);
+static void post_button (ml_session, void *vw);
+
+ml_bulletins
+new_ml_bulletins (pool pool, ml_session session, const char *conninfo,
+                 const char *section_name)
+{
+  ml_bulletins w = pmalloc (pool, sizeof *w);
+  db_handle dbh;
+  st_handle sth;
+
+  w->ops = &bulletins_ops;
+  w->pool = pool;
+  w->session = session;
+  w->conninfo = conninfo;
+  w->first_item = 0;
+  w->nr_items = 10;
+
+  /* Get the sectionid. */
+  dbh = get_db_handle (conninfo, DBI_THROW_ERRORS);
+
+  sth = st_prepare_cached
+    (dbh,
+     "select resid from ml_resources where name = ?", DBI_STRING);
+  st_execute (sth, section_name);
+
+  st_bind (sth, 0, w->sectionid, DBI_INT);
+
+  if (!st_fetch (sth)) return 0;
+
+  put_db_handle (dbh);
+
+  /* Create the buttons for the bottom of the page. The home/prev/next
+   * buttons get enabled (possibly) in update_buttons. The post button
+   * is always enabled, but only shown to eligible posters.
+   */
+  w->post = new_ml_button (pool, "Post");
+  ml_button_set_callback (w->post, post_button, session, w);
+  ml_button_set_popup (w->post, "bulletins_post_window");
+  ml_button_set_popup_size (w->post, 400, 300);
+
+  w->home = new_ml_button (pool, "Most recent");
+  w->prev = new_ml_button (pool, "&lt;&lt;");
+  w->next = new_ml_button (pool, "&gt;&gt;");
+  update_buttons (w);
+
+  return w;
+}
+
+/* Callback for the "home" button. */
+static void
+home_button (ml_session session, void *vw)
+{
+  ml_bulletins w = (ml_bulletins) vw;
+
+  w->first_item = 0;
+  update_buttons (w);
+}
+
+/* Callback for the "prev" button. */
+static void
+prev_button (ml_session session, void *vw)
+{
+  ml_bulletins w = (ml_bulletins) vw;
+
+  w->first_item -= w->nr_items;
+  if (w->first_item < 0) w->first_item = 0;
+  update_buttons (w);
+}
+
+/* Callback for the "next" button. */
+static void
+next_button (ml_session session, void *vw)
+{
+  ml_bulletins w = (ml_bulletins) vw;
+
+  w->first_item += w->nr_items;
+  update_buttons (w);
+}
+
+static inline void
+enable_home (ml_bulletins w)
+{
+  ml_button_set_callback (w->home, home_button, w->session, w);
+}
+
+static inline void
+disable_home (ml_bulletins w)
+{
+  ml_button_set_callback (w->home, 0, w->session, 0);
+}
+
+static inline void
+enable_prev (ml_bulletins w)
+{
+  ml_button_set_callback (w->prev, prev_button, w->session, w);
+}
+
+static inline void
+disable_prev (ml_bulletins w)
+{
+  ml_button_set_callback (w->prev, 0, w->session, 0);
+}
+
+static inline void
+enable_next (ml_bulletins w)
+{
+  ml_button_set_callback (w->next, next_button, w->session, w);
+}
+
+static inline void
+disable_next (ml_bulletins w)
+{
+  ml_button_set_callback (w->next, 0, w->session, 0);
+}
+
+/* This function updates the state of each button. It consults the
+ * database to find out how many articles are present.
+ */
+static void
+update_buttons (ml_bulletins w)
+{
+  db_handle dbh;
+  st_handle sth;
+  int count;
+
+  /* Get a database handle. */
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  /* Find out how many articles are present. */
+  sth = st_prepare_cached
+    (dbh, "select count (*) from ml_bulletins");
+  st_execute (sth);
+
+  st_bind (sth, 0, count, DBI_INT);
+  if (!st_fetch (sth))
+    pth_die ("select count(*) returned no rows!");
+
+  /* Make sure first_item is sensible. */
+  if (w->first_item >= count)
+    w->first_item = count - w->nr_items;
+  if (w->first_item < 0)
+    w->first_item = 0;
+
+  /* Decide which buttons to enable. */
+  if (w->first_item > w->nr_items)
+    enable_home (w);
+  else
+    disable_home (w);
+
+  if (w->first_item >= w->nr_items)
+    enable_prev (w);
+  else
+    disable_prev (w);
+
+  if (w->first_item + w->nr_items < count)
+    enable_next (w);
+  else
+    disable_next (w);
+}
+
+static void
+post_button (ml_session session, void *vw)
+{
+  ml_bulletins w = (ml_bulletins) vw;
+  db_handle dbh;
+  ml_window win;
+  ml_form_layout tbl;
+  ml_form form;
+  ml_form_submit sub;
+
+  /* Get a database handle. */
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  /* Is the current user allowed to post? It can happen that this
+   * function is called even if the user is not a legitimate poster.
+   * For example:
+   * (1) User displays the front page, with "Post" button, then logs
+   *     out in another window, then presses the "Post" button.
+   * (2) User is attempting to predict action IDs (note that the "Post"
+   *     button is registered, even if it not displayed).
+   * It's always a good idea to check permissions inside callback
+   * functions.
+   */
+  if (!can_post (dbh, w->sectionid, ml_session_userid (session)))
+    {
+      ml_error_window
+       (w->pool, session,
+        "You do not have permission to post in this section.",
+        ML_DIALOG_CLOSE_BUTTON);
+      return;
+    }
+
+  /* Display the posting form. */
+  win = new_ml_window (session, w->pool);
+  form = new_ml_form (w->pool);
+  ml_form_set_callback (form, post, session, w);
+  ml_widget_set_property (form, "method", "GET");
+  tbl = new_ml_form_layout (w->pool);
+
+  w->post_item = new_ml_form_textarea (w->pool, form, 3, 40);
+  ml_form_layout_pack (tbl, "Message:", w->post_item);
+
+  w->post_type = new_ml_form_select (w->pool, form);
+  ml_form_select_push_back (w->post_type, "Plain text");
+  ml_form_select_push_back (w->post_type, "*Smart* text");
+  ml_form_select_push_back (w->post_type, "HTML");
+  ml_form_select_set_selection (w->post_type, 1); /* XXX From preferences. */
+  ml_form_layout_pack (tbl, 0, w->post_type);
+
+  w->post_link = new_ml_form_text (w->pool, form);
+  ml_form_layout_pack (tbl, "URL:", w->post_link);
+
+  w->post_link_text = new_ml_form_text (w->pool, form);
+  ml_form_layout_pack (tbl, "Link text:", w->post_link_text);
+
+  sub = new_ml_form_submit (w->pool, form, "Post");
+  ml_form_layout_pack (tbl, 0, sub);
+
+  /* Pack everything up. */
+  ml_form_pack (form, tbl);
+  ml_window_pack (win, form);
+}
+
+static void
+post (ml_session session, void *vw)
+{
+  ml_bulletins w = (ml_bulletins) vw;
+  db_handle dbh;
+  st_handle sth;
+  const char *item, *item_type, *link, *link_text;
+  int type, userid;
+
+  /* Verify the details of the posting, otherwise do nothing, which just
+   * represents the form back to the user.
+   */
+  item = ml_form_input_get_value (w->post_item);
+  link = ml_form_input_get_value (w->post_link);
+  link_text = ml_form_input_get_value (w->post_link_text);
+
+  if (!item || strlen (item) == 0) return;
+
+  type = ml_form_select_get_selection (w->post_type);
+
+  /* Turn empty strings into nulls for the database. */
+  if (strlen (link) == 0) link = 0;
+  if (strlen (link_text) == 0) link_text = 0;
+
+  /* Set the item type. */
+  switch (type) {
+  case 0: item_type = "p"; break;
+  case 1: item_type = "s"; break;
+  case 2: item_type = "h"; break;
+  default: item_type = "s";
+  }
+
+  /* Get a database handle. */
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  /* Verify the user can post. See notes above. */
+  userid = ml_session_userid (session);
+  if (!can_post (dbh, w->sectionid, userid))
+    {
+      ml_error_window
+       (w->pool, session,
+        "You do not have permission to post in this section.",
+        ML_DIALOG_CLOSE_BUTTON);
+      return;
+    }
+
+  /* Insert the posting. */
+  sth = st_prepare_cached
+    (dbh,
+     "insert into ml_bulletins "
+     "(sectionid, authorid, item, item_type, link, link_text) "
+     "values (?, ?, ?, ?, ?, ?)",
+     DBI_INT, DBI_INT, DBI_STRING, DBI_STRING, DBI_STRING, DBI_STRING);
+  st_execute (sth, w->sectionid, userid, item, item_type, link, link_text);
+
+  /* Commit to the database. */
+  db_commit (dbh);
+  put_db_handle (dbh);
+
+  /* Present a confirmation page. */
+  ml_ok_window (w->pool, session,
+               "Item was successfully posted.",
+               ML_DIALOG_CLOSE_BUTTON | ML_DIALOG_CLOSE_RELOAD_OPENER);
+}
+
+static inline void
+show_item (io_handle io, int n, char *item, char *item_type, char *username,
+          char *posted_date, char *timediff, char *link, char *link_text)
+{
+  /* XXX Lots of issues in this block:
+   * (2) parsing/printing of dates
+   * (3) smart text support
+   * (4) escaping of link text
+   * (5) styling of the whole thing
+   */
+  io_fprintf (io, "<table width=\"100%%\"><tr>"
+             "<td rowspan=\"3\" valign=\"top\">%d.</td>",
+             n);
+  io_fprintf (io, "<td>Posted by <strong>%s</strong> on "
+             "<strong>%s</strong></td></tr>",
+             username, posted_date);
+  io_fprintf (io, "<tr><td>%s</td></tr>", item);
+  if (link && link_text)
+    io_fprintf (io, "<tr><td align=\"right\">"
+               "<a href=\"%s\">%s</a></td></tr>",
+               link, link_text);
+  else if (link)
+    io_fprintf (io, "<tr><td align=\"right\">"
+               "<a href=\"%s\">%s</a></td></tr>",
+               link, link);
+  else
+    io_fprintf (io, "<tr><td></td></tr>");
+  io_fprintf (io, "</table>");
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_bulletins w = (ml_bulletins) vw;
+  db_handle dbh;
+  st_handle sth;
+  char *item, *item_type, *username, *posted_date, *timediff,
+    *link, *link_text;
+  int n, is_poster;
+
+  /* Get a database handle. */
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  /* Is the current user allowed to post/remove articles? */
+  is_poster = can_post (dbh, w->sectionid, ml_session_userid (session));
+
+  /* Pull out the headlines. */
+  sth = st_prepare_cached
+    (dbh,
+     "select b.item, b.item_type, u.username, b.posted_date, "
+     "       current_timestamp - b.posted_date, "
+     "       b.link, b.link_text "
+     "from ml_bulletins b, ml_users u "
+     "where b.sectionid = ? and b.authorid = u.userid "
+     "order by 4 desc "
+     "limit ? "
+     "offset ?",
+     DBI_INT, DBI_INT, DBI_INT);
+  st_execute (sth, w->sectionid, w->nr_items, w->first_item);
+
+  st_bind (sth, 0, item, DBI_STRING);
+  st_bind (sth, 1, item_type, DBI_STRING);
+  st_bind (sth, 2, username, DBI_STRING);
+  st_bind (sth, 3, posted_date, DBI_STRING);
+  st_bind (sth, 4, timediff, DBI_STRING);
+  st_bind (sth, 5, link, DBI_STRING);
+  st_bind (sth, 6, link_text, DBI_STRING);
+
+  /* Display them. */
+  io_fprintf (io, "<table><tr><td><table>");
+
+  n = w->first_item + 1;
+
+  while (st_fetch (sth))
+    {
+      io_fprintf (io, "<tr><td>");
+
+      show_item (io, n, item, item_type, username, posted_date, timediff,
+                link, link_text);
+
+      io_fprintf (io, "</td></tr>");
+
+      n++;
+    }
+
+  /* Finish off the page with the buttons at the bottom. */
+  io_fprintf (io, "</table></td></tr><tr><td align=\"right\">");
+  if (is_poster)
+    ml_widget_repaint (w->post, session, windowid, io);
+  ml_widget_repaint (w->home, session, windowid, io);
+  ml_widget_repaint (w->prev, session, windowid, io);
+  ml_widget_repaint (w->next, session, windowid, io);
+  io_fprintf (io, "</td></tr></table>");
+
+  /* Be polite: give back the database handle. */
+  put_db_handle (dbh);
+}
+
+static int
+can_post (db_handle dbh, int sectionid, int userid)
+{
+  st_handle sth;
+
+  if (userid)
+    {
+      sth = st_prepare_cached
+       (dbh,
+        "select 1 from ml_bulletins_posters "
+        "where sectionid = ? and userid = ?", DBI_INT, DBI_INT);
+      st_execute (sth, sectionid, userid);
+
+      return st_fetch (sth);
+    }
+  else
+    return 0;
+}
diff --git a/widgets/ml_bulletins.h b/widgets/ml_bulletins.h
new file mode 100644 (file)
index 0000000..a9b481e
--- /dev/null
@@ -0,0 +1,59 @@
+/* Monolith bulletins (recent news spool).
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_bulletins.h,v 1.4 2003/02/22 15:34:32 rich Exp $
+ */
+
+#ifndef ML_BULLETINS_H
+#define ML_BULLETINS_H
+
+#include <pool.h>
+
+#include "monolith.h"
+
+struct ml_bulletins;
+typedef struct ml_bulletins *ml_bulletins;
+
+/* Function: new_ml_bulletins - monolith bulletins (recent news spool)
+ *
+ * The bulletins widget is a database-backed recent news spool.
+ * Designated administrators may insert short messages which
+ * appear at the top of the widget. Older items appear below.
+ * Ordinary users see the bulletins, newest at the top, and may
+ * also navigate through older items of news using previous/next
+ * buttons.
+ *
+ * Items are arranged into "sections", each with a unique name.
+ * This allows you to have different bulletins in different applications,
+ * or if building a website with MSP, on different pages in the site.
+ *
+ * News items are stored in a PostgreSQL database. You can find the
+ * schema in the @code{sql/ml_bulletins_create.sql} file in the
+ * source distribution.
+ *
+ * @code{new_ml_bulletins} creates a new widget. You must pass
+ * a @code{pool} for allocation and the current @code{session}
+ * object. For database access, a database connection @code{conninfo}
+ * must be passed in. The @code{section_name} is the name of the
+ * section (from the @code{ml_bulletins_sections} table).
+ *
+ * This function returns the widget, or @code{NULL} if the section
+ * could not be found in the database.
+ */
+extern ml_bulletins new_ml_bulletins (pool pool, ml_session session, const char *conninfo, const char *section_name);
+
+#endif /* ML_BULLETINS_H */
diff --git a/widgets/ml_display_table.h b/widgets/ml_display_table.h
new file mode 100644 (file)
index 0000000..fad387a
--- /dev/null
@@ -0,0 +1,87 @@
+/* Monolith display table class.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_display_table.h,v 1.1 2003/02/13 16:12:35 rich Exp $
+ */
+
+#ifndef ML_DISPLAY_TABLE_H
+#define ML_DISPLAY_TABLE_H
+
+#include <pool.h>
+
+#include "monolith.h"
+
+struct ml_display_table;
+typedef struct ml_display_table *ml_display_table;
+
+/* Function: new_ml_display_table - monolith display table widget
+ * Function: ml_display_table_set_row_repaint
+ * Function: ml_display_table_set_add_callback
+ * Function: ml_display_table_set_edit_callback
+ * Function: ml_display_table_set_delete_callback
+ *
+ * @code{ml_display_table} is a highly configurable widget allowing
+ * lists of items or tables to be displayed and edited. A display
+ * table shows a list of items, with Edit, Delete and Add buttons
+ * below, allowing the user to edit, delete or add rows.
+ *
+ * The underlying data model is a c2lib @code{vector}. Display table
+ * doesn't care what the vector contains (although it does sometimes
+ * pull out pointers to rows in the vector to pass to various callback
+ * functions below).
+ *
+ * To create a display table, you need to do several things:
+ *
+ * - Write a row repaint function. This is called to display each row.
+ *
+ * - Write an action function for 'Edit'. This is called when the user
+ * tries to edit a row, and usually displays a form in a new window.
+ *
+ * - (Optional) Write an action function for 'Delete'.
+ *
+ * - Write an 'Add row' widget (explained below).
+ *
+ * You then call @code{new_ml_display_table} to create the overall
+ * table. Then call @code{ml_display_table_set_row_repaint},
+ * @code{ml_display_table_set_add_callback},
+ * @code{ml_display_table_set_edit_callback} and optionally
+ * @code{ml_display_table_set_delete_callback} to set each
+ * callback function.
+ *
+ * @code{new_ml_display_table} takes usual pool and session arguments.
+ * It takes a @code{vector dm} which is the data model (ie. list of rows).
+ * The type of elements of the vector is not important to the display
+ * table.
+ *
+ * Bugs: The current display table does not support the following
+ * desirable features. These may be added in future releases.
+ *
+ * Pagination.
+ *
+ * Column headers.
+ *
+ * Extra action buttons.
+ *
+ * Add functions which open a new window.
+ */
+extern ml_display_table new_ml_display_table (pool pool, ml_session session, vector dm);
+extern ml_display_table ml_display_table_set_row_repaint (ml_display_table, );
+extern ml_display_table ml_display_table_set_add_callback (...);
+extern ml_display_table ml_display_table_set_edit_callback (...);
+extern ml_display_table ml_display_table_set_delete_callback (...);
+
+#endif /* ML_DISPLAY_TABLE */
diff --git a/widgets/ml_login_nopw.c b/widgets/ml_login_nopw.c
new file mode 100644 (file)
index 0000000..be36261
--- /dev/null
@@ -0,0 +1,453 @@
+/* Monolith login widget (email validation, no password).
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_login_nopw.c,v 1.8 2003/02/22 15:34:32 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+#include <pthr_iolib.h>
+#include <pthr_dbi.h>
+#include <pthr_cgi.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_flow_layout.h"
+#include "ml_table_layout.h"
+#include "ml_text_label.h"
+#include "ml_button.h"
+#include "ml_window.h"
+#include "ml_form.h"
+#include "ml_form_text.h"
+#include "ml_form_submit.h"
+#include "ml_dialog.h"
+#include "ml_login_nopw.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations login_nopw_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_login_nopw
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_session session;          /* Current session. */
+  const char *conninfo;                /* Database connection. */
+
+  /* The following is displayed when no user is logged in: */
+  ml_button out;
+
+  /* The following is displayed when a user is logged in: */
+  ml_flow_layout in;
+  ml_text_label in_name;
+
+  /* Form input for login form. */
+  ml_form_text email_input;
+
+  /* The email callback action - we need to unregister this before it
+   * is re-registered, for security reasons.
+   */
+  const char *actionid;
+
+  /* The email address which is trying to be registered. */
+  const char *email;
+
+  /* Secret string sent in the email. */
+  const char *secret;
+};
+
+/* Callback function prototypes. */
+static void login_button (ml_session, void *);
+static void login_send_email (ml_session, void *);
+static void login (ml_session, void *);
+static void logout_button (ml_session, void *);
+
+ml_login_nopw
+new_ml_login_nopw (pool pool, ml_session session, const char *conninfo)
+{
+  ml_login_nopw w = pmalloc (pool, sizeof *w);
+  ml_button button;
+
+  w->ops = &login_nopw_ops;
+  w->pool = pool;
+  w->session = session;
+  w->conninfo = conninfo;
+  w->actionid = 0;
+  w->email = 0;
+  w->secret = 0;
+
+  /* Generate the widget which is displayed when no user is logged in: */
+  w->out = new_ml_button (pool, "Login ...");
+  ml_button_set_callback (w->out, login_button, session, w);
+  ml_button_set_popup (w->out, "login_nopw_window");
+  ml_button_set_popup_size (w->out, 400, 300);
+
+  /* Generate the widget which is displayed when a user is logged in: */
+  w->in = new_ml_flow_layout (pool);
+
+  w->in_name = new_ml_text_label (pool, 0);
+  ml_widget_set_property (w->in_name, "font.weight", "bold");
+  ml_flow_layout_pack (w->in, w->in_name);
+
+  button = new_ml_button (pool, "Logout");
+  ml_button_set_callback (button, logout_button, session, w);
+  ml_flow_layout_pack (w->in, button);
+
+  return w;
+}
+
+static void
+login_button (ml_session session, void *vw)
+{
+  ml_login_nopw w = (ml_login_nopw) vw;
+  ml_window win;
+  ml_form form;
+  ml_table_layout tbl;
+  ml_text_label text;
+  ml_form_submit submit;
+
+  /* Create the login window. */
+  win = new_ml_window (session, w->pool);
+  form = new_ml_form (w->pool);
+  tbl = new_ml_table_layout (w->pool, 2, 2);
+
+  ml_form_set_callback (form, login_send_email, session, w);
+  ml_widget_set_property (form, "method", "GET");
+
+  text = new_ml_text_label
+    (w->pool,
+     "To log in to this site, or to create a user account, please type "
+     "your email address in the box below.\n\n"
+     "You will be sent a single confirmation email which contains a "
+     "special link that gives you access to the site.\n\n"
+     "Your email address is stored in our database, but you will not "
+     "receive any further emails, nor will your email address be shared "
+     "with others or displayed on the site (unless you specifically "
+     "ask for this in your settings).\n\n");
+  ml_table_layout_pack (tbl, text, 0, 0);
+  ml_table_layout_set_colspan (tbl, 0, 0, 2);
+
+  w->email_input = new_ml_form_text (w->pool, form);
+  ml_table_layout_pack (tbl, w->email_input, 1, 0);
+
+  submit = new_ml_form_submit (w->pool, form, "Submit");
+  ml_table_layout_pack (tbl, submit, 1, 1);
+
+  ml_form_pack (form, tbl);
+  ml_window_pack (win, form);
+}
+
+static inline const char *
+generate_secret (pool pool)
+{
+  int fd, i;
+  unsigned char buffer[16];
+  char *secret = pmalloc (pool, 33 * sizeof (char));
+
+  fd = open ("/dev/urandom", O_RDONLY);
+  if (fd == -1) abort ();
+  if (read (fd, buffer, 16) != 16) abort ();
+  close (fd);
+
+  for (i = 0; i < 16; ++i)
+    sprintf (secret + i*2, "%02x", buffer[i]);
+
+  return secret;
+}
+
+static const char *clean_up_string (pool pool, const char *text);
+
+static void
+login_send_email (ml_session session, void *vw)
+{
+  ml_login_nopw w = (ml_login_nopw) vw;
+  const char *email_c, *windowid;
+  char *email;
+  io_handle sendmail;
+  const char *sendmail_cmd
+    = "/usr/sbin/sendmail -t -i -f do_not_reply@annexia.org";
+  const char *site = ml_session_host_header (session);
+  const char *canonical_path = ml_session_canonical_path (session);
+  ml_window win;
+  ml_dialog dlg;
+
+  /* Create the window, and get the windowid which is passed in the
+   * email.
+   */
+  win = new_ml_window (session, w->pool);
+  windowid = _ml_window_get_windowid (win);
+
+  /* Get the email address which was typed in, and tidy it up a bit. */
+  email_c = ml_form_input_get_value (w->email_input);
+  if (!email_c) return;
+  email = pstrdup (w->pool, email_c);
+  ptrim (email);
+  pstrlwr (email);
+  if (strlen (email) == 0) return;
+  if (strchr (email, '@') == 0) return;
+  w->email = clean_up_string (w->pool, email);
+
+  /* Action IDs are predictable, so if all we did was to send back
+   * an action ID, then this would not offer security, since someone
+   * could guess the next action ID, and thus register as another
+   * user. Thus we also generate and send back a secret random string,
+   * and in the login() function we check that the secret string
+   * was passed back to us, proving that the user really received
+   * the email.
+   */
+  w->secret = generate_secret (w->pool);
+
+  /* Unregister old actionid, if there was one. For security reasons, since
+   * otherwise a user would be able to register as anyone in the following
+   * way:
+   * (1) Request registration for realname@example.com
+   * (2) Request registration for rich@annexia.org
+   * (3) Click confirmation of email from (1)
+   * (4) Registered as 'rich@annexia.org'!
+   * By unregistering the action, we remove this possibility (hopefully,
+   * at least, but if anyone else can think of a way around this, please
+   * tell me).
+   */
+  if (w->actionid) ml_unregister_action (session, w->actionid);
+
+  /* Register a callback action. */
+  w->actionid = ml_register_action (session, login, w);
+
+  /* Run sendmail. */
+  sendmail = io_popen (sendmail_cmd, "w");
+  if (!sendmail)
+    pth_die ("could not invoke sendmail");
+
+  io_fprintf
+    (sendmail,
+     "X-Monolith-Trace: %s %s %s\n"
+     "From: DO NOT REPLY TO THIS EMAIL <do_not_reply@annexia.org>\n"
+     "To: %s\n"
+     "Subject: Email validation from %s\n"
+     "\n"
+     "Someone, possibly you, typed your email address into %s.\n"
+     "If this was not you, then our sincere apologies; please ignore this\n"
+     "e-mail.\n"
+     "\n"
+     "To complete the log in to the site, you need to visit the link\n"
+     "below:\n"
+     "\n"
+     "http://%s%s?ml_window=%s&ml_action=%s&secret=%s\n"
+     "\n"
+     "Do not reply to this email!\n"
+     "\n"
+     "Notes for geeks:\n"
+     "\n"
+     "1. You need to use the same browser to fetch this URL. So 'wget'\n"
+     "   won't work. Instead, paste the URL if you cannot click on it\n"
+     "   directly.\n"
+     "2. This URL is valid until your current session ends.\n",
+     ml_session_get_peernamestr (session),
+     ml_session_host_header (session),
+     ml_session_canonical_path (session),
+     email, site, site, site, canonical_path, windowid, w->actionid,
+     w->secret);
+
+  io_pclose (sendmail);
+
+  /* Display a confirmation page. */
+  dlg = new_ml_dialog (w->pool);
+  ml_dialog_set_text
+    (dlg,
+     "A confirmation email was sent.\n\n"
+     "Click on the link in the email to complete your site registration / "
+     "login.");
+  ml_dialog_add_close_button (dlg, "Close window", 0);
+}
+
+/* Remove CRs and LFs from the string. */
+static const char *
+clean_up_string (pool pool, const char *text)
+{
+  if (strpbrk (text, "\n\r"))
+    {
+      char *copy = pstrdup (pool, text);
+      char *t = copy;
+
+      while ((t = strpbrk (t, "\n\r")) != 0)
+       *t++ = ' ';
+
+      return copy;
+    }
+  else
+    return text;               /* String is safe. */
+}
+
+static void
+login (ml_session session, void *vw)
+{
+  ml_login_nopw w = (ml_login_nopw) vw;
+  cgi submitted_args;
+  const char *secret;
+  db_handle dbh;
+  st_handle sth;
+  int userid;
+  ml_window win;
+  ml_dialog dlg;
+
+  /* Before proceeding, check the secret. */
+  submitted_args = _ml_session_submitted_args (session);
+  secret = cgi_param (submitted_args, "secret");
+
+  if (!secret || strlen (secret) == 0 ||
+      !w->secret || strcmp (secret, w->secret) != 0)
+    {
+      /* Failed. */
+      /* XXX Eventually ml_dialog will have a close window button. */
+      win = new_ml_window (session, w->pool);
+      dlg = new_ml_dialog (w->pool);
+      ml_dialog_set_text
+       (dlg,
+        "Email validation failed.\n\n"
+        "If you copied and pasted the URL out of an email message, please "
+        "make sure you copied it correctly.");
+      ml_window_pack (win, dlg);
+
+      return;
+    }
+
+  /* Secret is OK. Email address is valid. Log in or create a user account. */
+  dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+  sth = st_prepare_cached
+    (dbh,
+     "select userid from ml_users where email = ?", DBI_STRING);
+  st_execute (sth, w->email);
+
+  st_bind (sth, 0, userid, DBI_INT);
+
+  if (st_fetch (sth))          /* Existing account. */
+    {
+      sth = st_prepare_cached
+       (dbh,
+        "update ml_users set lastlogin_date = current_date, "
+        "nr_logins = nr_logins + 1 where userid = ?", DBI_INT);
+      st_execute (sth, userid);
+    }
+  else                         /* New account. */
+    {
+      char *username, *t;
+
+      /* Extract the username from the email address. Usernames are not
+       * unique (email addresses are), so just use the first part of
+       * the email address, before the first '@'.
+       */
+      username = pstrdup (w->pool, w->email);
+      if (!(t = strchr (username, '@'))) abort ();
+      *t = '\0';
+
+      sth = st_prepare_cached
+       (dbh,
+        "insert into ml_users (email, username, lastlogin_date, nr_logins) "
+        "values (?, ?, current_date, 1)",
+        DBI_STRING, DBI_STRING);
+      st_execute (sth, w->email, username);
+
+      /* Get the userid. */
+      userid = st_serial (sth, "ml_users_userid_seq");
+    }
+
+  /* Commit changes to the database. */
+  db_commit (dbh);
+
+  ml_session_login (session, userid, "/", "+1y");
+
+  /* Success. */
+  /* XXX Eventually ml_dialog will have a close window button. */
+  win = new_ml_window (session, w->pool);
+  dlg = new_ml_dialog (w->pool);
+  ml_dialog_set_text
+    (dlg,
+     "You are now logged into this site.");
+  ml_window_pack (win, dlg);
+}
+
+static void
+logout_button (ml_session session, void *vw)
+{
+  //ml_login_nopw w = (ml_login_nopw) vw;
+
+  ml_session_logout (session, "/");
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_login_nopw w = (ml_login_nopw) vw;
+  int userid;
+
+  /* Depending on whether a user is currently logged in or not, we display
+   * different things. This has to happen in the 'repaint' function
+   * because the currently logged in user can change unexpectedly
+   * when other people log in to other applications.
+   */
+  userid = ml_session_userid (session);
+
+  if (!userid)                 /* Not logged in. */
+    {
+      ml_widget_repaint (w->out, session, windowid, io);
+    }
+  else                         /* Logged in. */
+    {
+      db_handle dbh;
+      st_handle sth;
+      const char *email = 0;
+
+      /* Update the current user information string. */
+      dbh = get_db_handle (w->conninfo, DBI_THROW_ERRORS);
+
+      sth = st_prepare_cached
+       (dbh,
+        "select email from ml_users where userid = ?",
+        DBI_INT);
+      st_execute (sth, userid);
+
+      st_bind (sth, 0, email, DBI_STRING);
+
+      st_fetch (sth);          /* XXX or die ... */
+
+      ml_widget_set_property (w->in_name, "text", email);
+
+      /* Repaint. */
+      ml_widget_repaint (w->in, session, windowid, io);
+    }
+}
diff --git a/widgets/ml_login_nopw.h b/widgets/ml_login_nopw.h
new file mode 100644 (file)
index 0000000..402175f
--- /dev/null
@@ -0,0 +1,50 @@
+/* Monolith login widget (email validation, no password).
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_login_nopw.h,v 1.2 2003/02/22 15:34:33 rich Exp $
+ */
+
+#ifndef ML_LOGIN_NOPW_H
+#define ML_LOGIN_NOPW_H
+
+#include <pool.h>
+
+#include "monolith.h"
+
+struct ml_login_nopw;
+typedef struct ml_login_nopw *ml_login_nopw;
+
+/* Function: new_ml_login_nopw - login widget (email validation, no password)
+ *
+ * Login widgets provide a place for users to login, logout and register
+ * for the service. This particular login widget uses only email validation,
+ * and doesn't require passwords (or onerous registration steps). A user
+ * will see a box inviting them to type their email address. A confirmation
+ * email is sent to the user containing a link which they click. This
+ * authenticates them to the site, at which point they are logged in.
+ * Logged in users are shown a logout button.
+ *
+ * The @code{ml_login_nopw} widget requires the user tables, from
+ * @code{sql/monolith_users_create.sql}.
+ *
+ * @code{new_ml_login_nopw} creates a new login
+ * widget. @code{conninfo} should point to the PostgreSQL database
+ * configured with the user tables.
+ */
+extern ml_login_nopw new_ml_login_nopw (pool pool, ml_session session, const char *conninfo);
+
+#endif /* ML_LOGIN_NOPW_H */
diff --git a/widgets/ml_msp.c b/widgets/ml_msp.c
new file mode 100644 (file)
index 0000000..49f0d6f
--- /dev/null
@@ -0,0 +1,554 @@
+/* Monolith server-parsed pages (.msp's).
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_msp.c,v 1.10 2003/02/22 15:34:33 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <assert.h>
+
+#include <pcre.h>
+
+#include <pool.h>
+#include <pstring.h>
+#include <pre.h>
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_flow_layout.h"
+#include "ml_label.h"
+#include "ml_msp.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations msp_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_msp
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_session session;          /* Current session. */
+  const char *conninfo;                /* Database connection. */
+  const char *rootdir;         /* Document root. */
+  const char *filename;                /* MSP file, relative to rootdir. */
+  const char *pathname;                /* Full path to file. */
+  vector widgetpath;           /* Path when loading widgets (may be NULL). */
+  ml_flow_layout w;            /* MSP is really just a flow layout. */
+};
+
+static void init_msp (void) __attribute__((constructor));
+static void free_msp (void) __attribute__((destructor));
+static int  parse_file (ml_msp w, int fd);
+static int  verify_relative_path (const char *s);
+static int  verify_filename (const char *s);
+
+/* Global variables. */
+static pool msp_pool;
+static const pcre *re_openclose, *re_ws;
+
+/* Initialise the library. */
+static void
+init_msp ()
+{
+  msp_pool = new_subpool (global_pool);
+  re_openclose = precomp (msp_pool, "<%|%>", 0);
+  re_ws = precomp (msp_pool, "[ \t]+", 0);
+}
+
+/* Free up global memory used by the library. */
+static void
+free_msp ()
+{
+  delete_pool (msp_pool);
+}
+
+ml_msp
+new_ml_msp (pool pool, ml_session session, const char *conninfo,
+           const char *rootdir, const char *filename)
+{
+  ml_msp w = pmalloc (pool, sizeof *w);
+  int fd;
+
+  /* Security checks on the rootdir and filename. */
+  if (rootdir[0] != '/')
+    pth_die ("ml_msp.c: rootdir must start with '/'\n");
+
+  if (!verify_relative_path (filename))
+    return 0;
+
+  w->ops = &msp_ops;
+  w->pool = pool;
+  w->session = session;
+  w->conninfo = conninfo;
+  w->rootdir = rootdir;
+  w->filename = filename;
+  w->widgetpath = 0;
+  w->w = new_ml_flow_layout (pool);
+
+  /* Create the full path to the file. */
+  w->pathname = psprintf (pool, "%s/%s", rootdir, filename);
+
+  /* Open it. */
+  fd = open (w->pathname, O_RDONLY);
+  if (fd == -1)
+    {
+      perror (psprintf (pool, "ml_msp.c: %s", w->pathname));
+      return 0;
+    }
+
+  /* Read and parse the file. */
+  if (parse_file (w, fd) == -1)
+    {
+      close (fd);
+      return 0;                        /* parse_file prints an error. */
+    }
+
+  close (fd);
+
+  return w;
+}
+
+static int parse_directive (ml_msp w, const char *directive);
+static const char *load_file (pool tmp, int fd);
+
+static int
+parse_file (ml_msp w, int fd)
+{
+  pool pool = w->pool, tmp = new_subpool (pool);
+  ml_label label;
+  vector v;
+  const char *file;
+  int i;
+  char state = 'o';
+
+  /* Load the file into a temporary buffer. */
+  file = load_file (tmp, fd);
+  if (!file) return -1;
+
+  /* Using pstrresplit2 we can split up the file into units like
+   * this: [ "some HTML", "<%", "directive", "%>", "some more HTML", ... ]
+   * This makes parsing the file much simpler.
+   */
+  v = pstrresplit2 (pool, file, re_openclose);
+
+  for (i = 0; i < vector_size (v); ++i)
+    {
+      const char *s;
+      char type;
+
+      vector_get (v, i, s);
+
+#if 0                          /* Debug. */
+      fprintf (stderr, "ml_msp.c: reading %s\n", pstrndup (tmp, s, 20));
+#endif
+
+      if (strcmp (s, "<%") == 0) type = '(';
+      else if (strcmp (s, "%>") == 0) type = ')';
+      else type = '-';
+
+      switch (state)
+       {
+       case 'o':               /* Waiting for opening <% */
+         if (type == '(')
+           /* Found it. We are now inside a directive. */
+           state = 'i';
+         else
+           {
+             /* Must be HTML. Output it as a label. */
+             label = new_ml_label (pool, s);
+             ml_flow_layout_push_back (w->w, label);
+           }
+         break;
+       case 'i':               /* Inside a directive. */
+         if (type == '-')
+           {
+             /* Parse the directive. */
+             if (parse_directive (w, s) == -1)
+               return -1;
+             state = 'c';
+           }
+         else if (type == ')') /* Allow <%%> - just ignore it. */
+           state = 'o';
+         else if (type != ')')
+           {
+             /* It's an error. */
+             fprintf (stderr,
+                      "ml_msp.c: %s: unexpected '%s' inside directive.\n",
+                      w->filename, s);
+             return -1;
+           }
+         break;
+       case 'c':               /* Expecting %>. */
+         if (type == ')')
+           /* Found it. We are now back in HTML. */
+           state = 'o';
+         else
+           {
+             fprintf (stderr,
+                      "ml_msp.c: %s: unexpected '%s' inside directive.\n",
+                      w->filename, s);
+             return -1;
+           }
+       } /* switch (state) */
+    } /* for */
+
+  /* Check our final state, which must be 'o'. */
+  if (state != 'o')
+    {
+      fprintf (stderr, "ml_msp.c: %s: unclosed '<%%' in file.\n", w->filename);
+      return -1;
+    }
+
+  delete_pool (tmp);
+  return 0;
+}
+
+static int
+do_include (ml_msp w, const char *include_file)
+{
+  pool pool = w->pool;
+  char *dir, *t, *try;
+  int fd;
+
+  /* Verify that this is a plain, ordinary filename. */
+  if (!verify_filename (include_file))
+    return -1;
+
+  /* Locate the included file, relative to the current filename. Never leave
+   * the current root, however.
+   */
+  dir = pstrdup (pool, w->filename);
+  while (strlen (dir) > 0)
+    {
+      t = strrchr (dir, '/');
+      if (t)
+       {
+         *t = '\0';
+         try = psprintf (pool, "%s/%s/%s", w->rootdir, dir, include_file);
+       }
+      else
+       {
+         *dir = '\0';
+         try = psprintf (pool, "%s/%s", w->rootdir, include_file);
+       }
+
+      fd = open (try, O_RDONLY);
+      if (fd >= 0)
+       goto found_it;
+    }
+
+  /* Not found. */
+  fprintf (stderr, "ml_msp.c: include: %s: file not found.\n", include_file);
+  return -1;
+
+ found_it:
+  /* Parse the included file. */
+  if (parse_file (w, fd) == -1)
+    {
+      close (fd);
+      return -1;               /* parse_file prints an error. */
+    }
+
+  close (fd);
+
+  return 0;
+}
+
+static int
+do_widget (ml_msp w, const char *libfile, const char *new_fn,
+          const vector args)
+{
+  void *lib, *new_sym;
+  const char *filename, *error, *arg[5];
+  ml_widget widget;
+  int i;
+
+  if (strcmp (libfile, "-") == 0)
+    filename = 0;              /* Search in the current executable. */
+  else if (libfile[0] != '/')  /* Relative to the widget path. */
+    {
+      filename = libfile;
+
+      if (w->widgetpath)
+       for (i = 0; i < vector_size (w->widgetpath); ++i)
+         {
+           const char *path;
+           const char *try;
+
+           vector_get (w->widgetpath, i, path);
+           try = psprintf (w->pool, "%s/%s", path, libfile);
+           if (access (try, X_OK) == 0)
+             {
+               filename = try;
+               break;
+             }
+         }
+    }
+  else                         /* Absolute path. */
+    filename = libfile;
+
+  lib = dlopen (filename,
+#ifndef __OpenBSD__
+               RTLD_NOW
+#else
+               O_RDWR
+#endif
+               );
+  if (lib == 0)
+    {
+      fprintf (stderr, "ml_msp.c: %s: %s\n", libfile, dlerror ());
+      return -1;
+    }
+
+  /* Does the new function exist? */
+  new_sym = dlsym (lib, new_fn);
+  if ((error = dlerror ()) != 0)
+    {
+      fprintf (stderr, "ml_msp.c: %s: %s: %s\n", libfile, new_fn, error);
+      dlclose (new_sym);
+      return -1;
+    }
+
+  /* Make sure we close this library when the widget is deleted. */
+  pool_register_cleanup_fn (w->pool, (void (*)(void *)) dlclose, lib);
+
+  /* Formulate our call.
+   * XXX There needs to be a generic method for doing this in c2lib XXX
+   */
+  arg[0] = arg[1] = arg[2] = arg[3] = arg[4] = 0;
+  if (vector_size (args) >= 1) vector_get (args, 0, arg[0]);
+  if (vector_size (args) >= 2) vector_get (args, 1, arg[1]);
+  if (vector_size (args) >= 3) vector_get (args, 2, arg[2]);
+  if (vector_size (args) >= 4) vector_get (args, 3, arg[3]);
+  if (vector_size (args) >= 5) vector_get (args, 4, arg[4]);
+
+  if (vector_size (args) == 1 && strcmp (arg[0], "pool") == 0)
+    {
+      ml_widget (*fn) (pool) = (ml_widget (*) (pool)) new_sym;
+
+      widget = fn (w->pool);
+      if (widget) ml_flow_layout_push_back (w->w, widget);
+    }
+  else if (vector_size (args) == 2 && strcmp (arg[0], "pool") == 0 &&
+          strcmp (arg[1], "session") == 0)
+    {
+      ml_widget (*fn) (pool, ml_session) =
+       (ml_widget (*) (pool, ml_session)) new_sym;
+
+      widget = fn (w->pool, w->session);
+      if (widget) ml_flow_layout_push_back (w->w, widget);
+    }
+  else if (vector_size (args) == 3 && strcmp (arg[0], "pool") == 0 &&
+          strcmp (arg[1], "session") == 0 &&
+          strcmp (arg[2], "conninfo") == 0)
+    {
+      ml_widget (*fn) (pool, ml_session, const char *) =
+       (ml_widget (*) (pool, ml_session, const char *)) new_sym;
+
+      widget = fn (w->pool, w->session, w->conninfo);
+      if (widget) ml_flow_layout_push_back (w->w, widget);
+    }
+  else if (vector_size (args) == 4 && strcmp (arg[0], "pool") == 0 &&
+          strcmp (arg[1], "session") == 0 &&
+          strcmp (arg[2], "conninfo") == 0)
+    {
+      ml_widget (*fn) (pool, ml_session, const char *, const char *) =
+      (ml_widget (*) (pool, ml_session, const char *, const char *)) new_sym;
+
+      widget = fn (w->pool, w->session, w->conninfo, arg[3]);
+      if (widget) ml_flow_layout_push_back (w->w, widget);
+    }
+  else if (vector_size (args) == 5 && strcmp (arg[0], "pool") == 0 &&
+          strcmp (arg[1], "session") == 0 &&
+          strcmp (arg[2], "conninfo") == 0)
+    {
+      ml_widget (*fn) (pool, ml_session, const char *, const char *, const char *) =
+      (ml_widget (*) (pool, ml_session, const char *, const char *, const char *)) new_sym;
+
+      widget = fn (w->pool, w->session, w->conninfo, arg[3], arg[4]);
+      if (widget) ml_flow_layout_push_back (w->w, widget);
+    }
+  else
+    abort ();                  /* XXX Not yet implemented */
+
+  return 0;
+}
+
+static int
+do_widgetpath (ml_msp w, const vector dirs)
+{
+  fprintf (stderr, "XXX not impl XXX\n");
+  return -1;
+}
+
+static int
+parse_directive (ml_msp w, const char *directive)
+{
+  vector tokens;
+  pool pool = w->pool;
+  const char *command;
+
+  /* Split the directive up into tokens.
+   * XXX This is presently not very intelligent. It will split
+   * string arguments. There is a proposed solution for this in
+   * the form of a 'pstrtok' function in c2lib. At the moment this
+   * will suffice.
+   */
+  tokens = pstrresplit (pool, directive, re_ws);
+  if (vector_size (tokens) < 1)
+    return 0;                  /* Just ignore it. */
+
+  /* Get the command. */
+  vector_pop_front (tokens, command);
+
+  if (strcasecmp (command, "include") == 0)
+    {
+      const char *file;
+
+      if (vector_size (tokens) != 1)
+       {
+         fprintf (stderr, "ml_msp.c: %s: include: needs one filename\n",
+                  w->filename);
+         return -1;
+       }
+      vector_pop_front (tokens, file);
+      return do_include (w, file);
+    }
+  else if (strcasecmp (command, "widget") == 0)
+    {
+      const char *file;
+      const char *new_fn;
+
+      if (vector_size (tokens) < 3)
+       {
+         fprintf (stderr, "ml_msp.c: %s: widget: bad parameters\n",
+                  w->filename);
+         return -1;
+       }
+      vector_pop_front (tokens, file);
+      vector_pop_front (tokens, new_fn);
+      return do_widget (w, file, new_fn, tokens);
+    }
+  else if (strcasecmp (command, "widgetpath") == 0)
+    {
+      return do_widgetpath (w, tokens);
+    }
+  else
+    {
+      fprintf (stderr, "ml_msp.c: %s: %s: unknown directive\n",
+              w->filename, command);
+      return -1;
+    }
+}
+
+/* Load a file into memory from fd, allocating space from the
+ * temporary pool tmp.
+ */
+static const char *
+load_file (pool tmp, int fd)
+{
+  char *file = 0;
+  int r, sz = 0;
+  const int n = 16385;         /* [sic] - see comment at end */
+
+  for (;;)
+    {
+      file = prealloc (tmp, file, sz + n);
+      r = read (fd, file + sz, n-1);
+
+      if (r < 0)
+       {
+         perror ("read");
+         return 0;
+       }
+      if (r == 0) break;       /* end of file */
+
+      sz += r;
+    }
+
+  /* Since tmp is a temporary pool, don't bother rounding the size of the
+   * buffer down to match the file size. Just make sure it's null-terminated.
+   * Note that one extra byte we reserved above.
+   */
+  file[sz] = '\0';
+
+  return file;
+}
+
+static int
+verify_relative_path (const char *s)
+{
+  if (s[0] == '/' ||
+      strncmp (s, "..", 2) == 0 ||
+      strstr (s, "/..") ||
+      strlen (s) == 0)
+    {
+      fprintf (stderr,
+              "ml_msp.c: security error: string is not a relative path.\n");
+      return 0;
+    }
+  return 1;
+}
+
+static int
+verify_filename (const char *s)
+{
+  if (strchr (s, '/') != 0 ||
+      s[0] == '.' ||
+      strlen (s) == 0)
+    {
+      fprintf (stderr,
+              "ml_msp.c: security error: string is not a plain filename.\n");
+      return 0;
+    }
+  return 1;
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_msp w = (ml_msp) vw;
+
+  /* Repaint the flow layout. */
+  if (w->w)
+    ml_widget_repaint (w->w, session, windowid, io);
+}
diff --git a/widgets/ml_msp.h b/widgets/ml_msp.h
new file mode 100644 (file)
index 0000000..e51ea70
--- /dev/null
@@ -0,0 +1,101 @@
+/* Monolith server-parsed pages (.msp's).
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_msp.h,v 1.4 2003/02/22 15:34:33 rich Exp $
+ */
+
+#ifndef ML_MSP_H
+#define ML_MSP_H
+
+#include "monolith.h"
+
+struct ml_msp;
+typedef struct ml_msp *ml_msp;
+
+/* Function: new_ml_msp - monolith server-parsed pages (.msp)
+ *
+ * Monolith server-parsed pages (@code{.msp} files) are ordinary
+ * HTML files which contain additional markup for embedding other
+ * monolith widgets and a few other useful features like server-side
+ * includes.
+ *
+ * A @code{msp} widget is a layout widget which can, like any other
+ * widget, be embedded anywhere. However, normally you will not be
+ * using the @code{msp} widget directly, but instead will use the
+ * @code{msp.so} application (in the @code{apps/} directory). You
+ * use the @code{msp.so} application by placing the following lines
+ * in the rws host configuration file:
+ *
+ * @code{begin rewrite}
+ *
+ * @code{ ...}
+ *
+ * @code{^/(.*\\.msp)$  /so-bin/msp.so?page=$1  last}
+ *
+ * @code{end rewrite}
+ *
+ * @code{msp root: /var/www/html}
+ *
+ * When the user requests @code{/dir/file.msp}, this is internally
+ * rewritten to a request for @code{/so-bin/msp.so?page=/dir/file.msp}.
+ * The @code{msp.so} program creates the @code{msp} widget with the
+ * arguments @code{rootdir = /var/www/html} and
+ * @code{filename = dir/file.msp}.
+ *
+ * The @code{msp} widget fetches @code{/var/www/html/dir/file.msp},
+ * interprets any special markup, and displays the page.
+ *
+ * The following special markup can appear in @code{msp} files:
+ *
+ * @code{<% widget [libfile.so|-] new_fn [param1 [param2 [...]]] %>}: Place a
+ * monolith widget here. The widget is loaded from file
+ * @code{libfile.so} which can either be a name (relative to
+ * the current widget path) or a pathname. To load a widget
+ * from core or the msp library itself, there is no need
+ * to specify the library file. Just use the special name
+ * @code{-}. Function @code{new_fn} is called to create the widget
+ * with the list of parameters given. Each parameter can either be
+ * a string (in "double quotes"), an integer, or one of the
+ * special identifiers @code{pool} or @code{session}.
+ *
+ * @code{<% widgetpath dir1 [dir2 [dir3 [...]]] %>}: Adds the directories
+ * listed to the current path which is searched for widget libraries. The
+ * standard library directories (eg. @code{/usr/lib}, @code{/lib} and
+ * others depending on the platform) are automatically included.
+ *
+ * @code{<% include file %>} includes a file. The file is searched
+ * for in the document root, starting at the same directory as the
+ * @code{.msp} file, and going up one directory at a time until we
+ * reach the document root. For security reasons, the filename
+ * cannot contain @code{/} or begin with a dot. The contents of the
+ * file can contain @code{msp} directives.
+ *
+ * @code{new_ml_msp} creates a new @code{msp} widget. The @code{rootdir}
+ * parameter is the document root. @code{msp} promises never to attempt
+ * to read or include a file outside the document root, although other
+ * types of file such as widget libraries can come from outside the
+ * document root. The @code{filename} parameter is the name of the
+ * @code{msp} file, taken relative to @code{rootdir}. For security
+ * reasons @code{filename} cannot contain any @code{..} elements, else
+ * @code{new_ml_msp} returns @code{NULL}.
+ *
+ * An @code{msp} widget is really just a specialised form of flow
+ * layout (see @ref{new_ml_flow_layout(3)}).
+ */
+extern ml_msp new_ml_msp (pool pool, ml_session session, const char *conninfo, const char *rootdir, const char *filename);
+
+#endif /* ML_MSP_H */
diff --git a/widgets/ml_user_directory.c b/widgets/ml_user_directory.c
new file mode 100644 (file)
index 0000000..70f1705
--- /dev/null
@@ -0,0 +1,159 @@
+/* Monolith user directory widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_user_directory.c,v 1.2 2003/02/22 15:34:33 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+#include <pre.h>
+#include <pthr_iolib.h>
+
+#include "monolith.h"
+#include "ml_widget.h"
+#include "ml_button.h"
+#include "ml_window.h"
+#include "ml_form.h"
+#include "ml_form_select.h"
+#include "ml_form_submit.h"
+#include "ml_table_layout.h"
+#include "ml_user_directory.h"
+
+static void repaint (void *, ml_session, const char *, io_handle);
+
+struct ml_widget_operations user_directory_ops =
+  {
+    repaint: repaint
+  };
+
+struct ml_user_directory
+{
+  struct ml_widget_operations *ops;
+  pool pool;                   /* Pool for allocations. */
+  ml_session session;          /* Current session. */
+  const char *conninfo;                /* Database connection. */
+  ml_form form;                        /* Form (can be NULL). */
+  int userid;                  /* Currently selected userid (0 = none). */
+  ml_widget top;               /* This is our top-level widget. */
+  ml_form_select select;       /* Selection widget. */
+  vector users;                        /* List of all users (vector of int). */
+};
+
+static void make_select (pool, ml_session, const char *,
+                        ml_form, int userid,
+                        ml_form_select *select_rtn,
+                        vector *users_rtn);
+
+ml_user_directory
+new_ml_user_directory (pool pool, ml_session session, const char *conninfo,
+                      ml_form form, int userid)
+{
+  ml_user_directory w = pmalloc (pool, sizeof *w);
+
+  w->ops = &user_directory_ops;
+  w->pool = pool;
+  w->session = session;
+  w->conninfo = conninfo;
+  w->userid = userid;
+
+  /* Create the table widget. */
+  if (form)                    /* We are in an existing form. */
+    {
+      w->form = form;
+      make_select (pool, session, conninfo, form, userid,
+                  &w->select, &w->users);
+      w->top = w->select;
+    }
+  else                         /* We are a standalone widget. */
+    {
+      ml_table_layout tbl;
+      ml_form_submit submit;
+
+      w->form = new_ml_form (w->pool);
+      tbl = new_ml_table_layout (pool, 1, 2);
+      make_select (pool, session, conninfo, w->form, userid,
+                  &w->select, &w->users);
+      ml_table_layout_pack (tbl, w->select, 0, 0);
+      submit = new_ml_form_submit (pool, w->form, "Go");
+      ml_table_layout_pack (tbl, submit, 0, 1);
+      ml_form_pack (w->form, tbl);
+
+      w->top = w->form;
+    }
+
+  return w;
+}
+
+static void
+make_select (pool pool, ml_session session, const char *conninfo,
+            ml_form form, int selected_userid,
+            ml_form_select *select, vector *users)
+{
+  db_handle dbh;
+  st_handle sth;
+  int userid;
+  const char *email, *given_name, *family_name;
+
+  *select = new_ml_form_select (pool, form);
+  *users = new_vector (pool, int);
+
+  dbh = get_db_handle (conninfo, DBI_THROW_ERRORS);
+
+  /* Pull out the list of users, and details. */
+  sth = st_prepare_cached
+    (dbh,
+     "select p.userid, u.email, u.given_name, u.family_name "
+     "from ml_userdir_prefs p, ml_users u "
+     "order by u.given_name, u.family_name, u.email");
+  st_execute (sth);
+
+  st_bind (sth, 0, userid, DBI_INT);
+  st_bind (sth, 1, email, DBI_STRING);
+  st_bind (sth, 2, given_name, DBI_STRING);
+  st_bind (sth, 3, family_name, DBI_STRING);
+
+  while (st_fetch (sth))
+    {
+      vector_push_back (*users, userid);
+      ml_form_select_push_back
+       (*select,
+        psprintf (pool, "%s %s <%s>", given_name, family_name, email));
+
+      if (userid == selected_userid)
+       ml_form_select_set_selection (*select,
+                                     ml_form_select_size (*select) - 1);
+    }
+
+  put_db_handle (dbh);
+}
+
+static void
+repaint (void *vw, ml_session session, const char *windowid, io_handle io)
+{
+  ml_user_directory w = (ml_user_directory) vw;
+
+  ml_widget_repaint (w->top, session, windowid, io);
+}
diff --git a/widgets/ml_user_directory.h b/widgets/ml_user_directory.h
new file mode 100644 (file)
index 0000000..105816d
--- /dev/null
@@ -0,0 +1,78 @@
+/* Monolith user directory widget.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: ml_user_directory.h,v 1.2 2003/02/22 15:34:33 rich Exp $
+ */
+
+#ifndef ML_USER_DIRECTORY_H
+#define ML_USER_DIRECTORY_H
+
+#include <pool.h>
+
+#include "monolith.h"
+#include "ml_form.h"
+
+struct ml_user_directory;
+typedef struct ml_user_directory *ml_user_directory;
+
+/* Function: new_ml_user_directory - monolith user directory widget
+ * Function: ml_user_directory_get_selection
+ * Function: ml_user_directory_set_selection
+ * Function: ml_user_directory_set_callback
+ *
+ * The user directory is a widget for picking a user on the system.
+ * It is very primitive at the moment, but in the future will become
+ * more advanced (able to cope with 100s-millions of people in the
+ * directory) with search features and so on.
+ *
+ * The user directory supports picking single users only at the moment,
+ * but we intend to extend it in future to support picking multiple
+ * users.
+ *
+ * The user directory can be used either a form input or as a
+ * standalone widget which works rather like a button.
+ *
+ * It requires the schema in @code{sql/ml_userdir_create.sql}.
+ *
+ * @code{new_ml_user_directory} creates a new user directory widget.
+ * The @code{pool}, @code{session} and @code{conninfo} arguments point
+ * to a pool for allocations, the current session and a database
+ * connection string. The @code{form} argument may be either a form
+ * object (see @ref{new_ml_form(3)}), or @code{NULL} if this widget is
+ * not going to be used in a form. @code{userid} is the initially
+ * selected user ID, or it may be @code{0} meaning that no user is
+ * selected initially.
+ *
+ * @code{ml_user_directory_(get|set)_selection} reads or changes the
+ * currently selected user ID.
+ *
+ * @code{ml_user_directory_set_callback} sets a function which is
+ * called when the currently selected user is changed. Callbacks
+ * should only be set when the widget is NOT in a form.
+ *
+ * When the widget is used in a form, then the currently selected user
+ * ID is only available when the form is submitted (ie. in the form's
+ * callback function). When the widget is not in a form, then you can
+ * set a callback for the widget, and call
+ * @code{ml_user_directory_get_selection} during this function.
+ */
+extern ml_user_directory new_ml_user_directory (pool pool, ml_session session, const char *conninfo, ml_form form, int userid);
+extern int ml_user_directory_get_selection (ml_user_directory w);
+extern void ml_user_directory_set_selection (ml_user_directory w, int userid);
+extern void ml_user_directory_set_callback (ml_user_directory w, void (*fn) (ml_session, void *), ml_session session, void *data);
+
+#endif /* ML_USER_DIRECTORY_H */